@edgible-team/cli 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +136 -0
- package/README.md +450 -0
- package/dist/client/api-client.js +1057 -0
- package/dist/client/index.js +21 -0
- package/dist/commands/agent.js +1280 -0
- package/dist/commands/ai.js +608 -0
- package/dist/commands/application.js +885 -0
- package/dist/commands/auth.js +570 -0
- package/dist/commands/base/BaseCommand.js +93 -0
- package/dist/commands/base/CommandHandler.js +7 -0
- package/dist/commands/base/command-wrapper.js +58 -0
- package/dist/commands/base/middleware.js +77 -0
- package/dist/commands/config.js +116 -0
- package/dist/commands/connectivity.js +59 -0
- package/dist/commands/debug.js +98 -0
- package/dist/commands/discover.js +144 -0
- package/dist/commands/examples/migrated-command-example.js +180 -0
- package/dist/commands/gateway.js +494 -0
- package/dist/commands/managedGateway.js +787 -0
- package/dist/commands/utils/config-validator.js +76 -0
- package/dist/commands/utils/gateway-prompt.js +79 -0
- package/dist/commands/utils/input-parser.js +120 -0
- package/dist/commands/utils/output-formatter.js +109 -0
- package/dist/config/app-config.js +99 -0
- package/dist/detection/SystemCapabilityDetector.js +1244 -0
- package/dist/detection/ToolDetector.js +305 -0
- package/dist/detection/WorkloadDetector.js +314 -0
- package/dist/di/bindings.js +99 -0
- package/dist/di/container.js +88 -0
- package/dist/di/types.js +32 -0
- package/dist/index.js +52 -0
- package/dist/interfaces/IDaemonManager.js +3 -0
- package/dist/repositories/config-repository.js +62 -0
- package/dist/repositories/gateway-repository.js +35 -0
- package/dist/scripts/postinstall.js +101 -0
- package/dist/services/AgentStatusManager.js +299 -0
- package/dist/services/ConnectivityTester.js +271 -0
- package/dist/services/DependencyInstaller.js +475 -0
- package/dist/services/LocalAgentManager.js +2216 -0
- package/dist/services/application/ApplicationService.js +299 -0
- package/dist/services/auth/AuthService.js +214 -0
- package/dist/services/aws.js +644 -0
- package/dist/services/daemon/DaemonManagerFactory.js +65 -0
- package/dist/services/daemon/DockerDaemonManager.js +395 -0
- package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
- package/dist/services/daemon/PodmanDaemonManager.js +369 -0
- package/dist/services/daemon/SystemdDaemonManager.js +221 -0
- package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
- package/dist/services/daemon/index.js +16 -0
- package/dist/services/edgible.js +3060 -0
- package/dist/services/gateway/GatewayService.js +334 -0
- package/dist/state/config.js +146 -0
- package/dist/types/AgentConfig.js +5 -0
- package/dist/types/AgentStatus.js +5 -0
- package/dist/types/ApiClient.js +5 -0
- package/dist/types/ApiRequests.js +5 -0
- package/dist/types/ApiResponses.js +5 -0
- package/dist/types/Application.js +5 -0
- package/dist/types/CaddyJson.js +5 -0
- package/dist/types/UnifiedAgentStatus.js +56 -0
- package/dist/types/WireGuard.js +5 -0
- package/dist/types/Workload.js +5 -0
- package/dist/types/agent.js +5 -0
- package/dist/types/command-options.js +5 -0
- package/dist/types/connectivity.js +5 -0
- package/dist/types/errors.js +250 -0
- package/dist/types/gateway-types.js +5 -0
- package/dist/types/index.js +48 -0
- package/dist/types/models/ApplicationData.js +5 -0
- package/dist/types/models/CertificateData.js +5 -0
- package/dist/types/models/DeviceData.js +5 -0
- package/dist/types/models/DevicePoolData.js +5 -0
- package/dist/types/models/OrganizationData.js +5 -0
- package/dist/types/models/OrganizationInviteData.js +5 -0
- package/dist/types/models/ProviderConfiguration.js +5 -0
- package/dist/types/models/ResourceData.js +5 -0
- package/dist/types/models/ServiceResourceData.js +5 -0
- package/dist/types/models/UserData.js +5 -0
- package/dist/types/route.js +5 -0
- package/dist/types/validation/schemas.js +218 -0
- package/dist/types/validation.js +5 -0
- package/dist/utils/FileIntegrityManager.js +256 -0
- package/dist/utils/PathMigration.js +219 -0
- package/dist/utils/PathResolver.js +235 -0
- package/dist/utils/PlatformDetector.js +277 -0
- package/dist/utils/console-logger.js +130 -0
- package/dist/utils/docker-compose-parser.js +179 -0
- package/dist/utils/errors.js +130 -0
- package/dist/utils/health-checker.js +155 -0
- package/dist/utils/json-logger.js +72 -0
- package/dist/utils/log-formatter.js +293 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/network-utils.js +217 -0
- package/dist/utils/output.js +182 -0
- package/dist/utils/passwordValidation.js +91 -0
- package/dist/utils/progress.js +167 -0
- package/dist/utils/sudo-checker.js +22 -0
- package/dist/utils/urls.js +32 -0
- package/dist/utils/validation.js +31 -0
- package/dist/validation/schemas.js +175 -0
- package/dist/validation/validator.js +67 -0
- package/package.json +83 -0
|
@@ -0,0 +1,1244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SystemCapabilityDetector = void 0;
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
class SystemCapabilityDetector {
|
|
44
|
+
/**
|
|
45
|
+
* Detect all system capabilities
|
|
46
|
+
*/
|
|
47
|
+
static async detectCapabilities() {
|
|
48
|
+
const cpu = this.detectCpu();
|
|
49
|
+
const memory = this.detectMemory();
|
|
50
|
+
const gpu = await this.detectGpu();
|
|
51
|
+
const platform = os.platform();
|
|
52
|
+
const gpuDriverSupport = await this.detectGpuDriverSupport(gpu);
|
|
53
|
+
const recommendedModels = this.recommendLLMModels(cpu, memory, gpu);
|
|
54
|
+
const hasEnoughResources = recommendedModels.some((m) => m.suitability === 'excellent' || m.suitability === 'good');
|
|
55
|
+
return {
|
|
56
|
+
cpu,
|
|
57
|
+
memory,
|
|
58
|
+
gpu,
|
|
59
|
+
platform,
|
|
60
|
+
hasEnoughResources,
|
|
61
|
+
recommendedModels,
|
|
62
|
+
gpuDriverSupport,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Detect CPU information
|
|
67
|
+
*/
|
|
68
|
+
static detectCpu() {
|
|
69
|
+
const cpus = os.cpus();
|
|
70
|
+
const firstCpu = cpus[0];
|
|
71
|
+
const model = firstCpu?.model || 'Unknown';
|
|
72
|
+
const cores = cpus.length;
|
|
73
|
+
const threads = cores; // os.cpus() returns logical cores
|
|
74
|
+
const architecture = os.arch();
|
|
75
|
+
const speed = firstCpu?.speed ? firstCpu.speed / 1000 : 0; // Convert MHz to GHz
|
|
76
|
+
return {
|
|
77
|
+
model,
|
|
78
|
+
cores,
|
|
79
|
+
threads,
|
|
80
|
+
architecture,
|
|
81
|
+
speed,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Detect memory information
|
|
86
|
+
*/
|
|
87
|
+
static detectMemory() {
|
|
88
|
+
const total = os.totalmem();
|
|
89
|
+
const free = os.freemem();
|
|
90
|
+
const used = total - free;
|
|
91
|
+
return {
|
|
92
|
+
total,
|
|
93
|
+
free,
|
|
94
|
+
used,
|
|
95
|
+
totalGB: this.bytesToGB(total),
|
|
96
|
+
freeGB: this.bytesToGB(free),
|
|
97
|
+
usedGB: this.bytesToGB(used),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Detect GPU information
|
|
102
|
+
*/
|
|
103
|
+
static async detectGpu() {
|
|
104
|
+
try {
|
|
105
|
+
// Try NVIDIA GPU first (most common for ML)
|
|
106
|
+
if (this.hasCommand('nvidia-smi')) {
|
|
107
|
+
return await this.detectNvidiaGpu();
|
|
108
|
+
}
|
|
109
|
+
// Try macOS GPU detection (Apple Silicon or Intel)
|
|
110
|
+
if (process.platform === 'darwin') {
|
|
111
|
+
return await this.detectMacOSGpu();
|
|
112
|
+
}
|
|
113
|
+
// Try AMD GPU detection (multiple methods)
|
|
114
|
+
if (process.platform === 'linux') {
|
|
115
|
+
// Try ROCm first (for data center GPUs)
|
|
116
|
+
if (this.hasCommand('rocm-smi')) {
|
|
117
|
+
const rocmGpu = await this.detectAmdGpu();
|
|
118
|
+
if (rocmGpu)
|
|
119
|
+
return rocmGpu;
|
|
120
|
+
}
|
|
121
|
+
// Try Linux AMD GPU detection (consumer GPUs)
|
|
122
|
+
const linuxAmdGpu = await this.detectAmdGpuLinux();
|
|
123
|
+
if (linuxAmdGpu)
|
|
124
|
+
return linuxAmdGpu;
|
|
125
|
+
// Fall back to generic PCI detection
|
|
126
|
+
return await this.detectGpuViaLspci();
|
|
127
|
+
}
|
|
128
|
+
// Try Windows GPU detection (includes AMD VRAM)
|
|
129
|
+
if (process.platform === 'win32') {
|
|
130
|
+
return await this.detectGpuViaWmic();
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Detect NVIDIA GPU using nvidia-smi
|
|
140
|
+
*/
|
|
141
|
+
static async detectNvidiaGpu() {
|
|
142
|
+
try {
|
|
143
|
+
const output = (0, child_process_1.execSync)('nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader,nounits', {
|
|
144
|
+
encoding: 'utf8',
|
|
145
|
+
timeout: 5000,
|
|
146
|
+
});
|
|
147
|
+
const lines = output.trim().split('\n');
|
|
148
|
+
if (lines.length === 0) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
// Get first GPU
|
|
152
|
+
const line = lines[0].trim();
|
|
153
|
+
const parts = line.split(',');
|
|
154
|
+
if (parts.length < 2) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const name = parts[0].trim();
|
|
158
|
+
const vramMB = parseInt(parts[1].trim(), 10);
|
|
159
|
+
const driverVersion = parts[2]?.trim();
|
|
160
|
+
return {
|
|
161
|
+
name,
|
|
162
|
+
vendor: 'NVIDIA',
|
|
163
|
+
vramGB: Math.round((vramMB / 1024) * 10) / 10, // Round to 1 decimal
|
|
164
|
+
driverVersion,
|
|
165
|
+
available: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Detect AMD GPU using rocm-smi (ROCm drivers, typically data center GPUs)
|
|
174
|
+
*/
|
|
175
|
+
static async detectAmdGpu() {
|
|
176
|
+
try {
|
|
177
|
+
const output = (0, child_process_1.execSync)('rocm-smi --showproductname --showmeminfo vram --showdriverversion', {
|
|
178
|
+
encoding: 'utf8',
|
|
179
|
+
timeout: 5000,
|
|
180
|
+
});
|
|
181
|
+
// Parse output (format varies)
|
|
182
|
+
const nameMatch = output.match(/Card series:\s*(.+)/i);
|
|
183
|
+
const vramMatch = output.match(/vram.*?(\d+)\s*MB/i);
|
|
184
|
+
const driverMatch = output.match(/Driver version:\s*(.+)/i);
|
|
185
|
+
return {
|
|
186
|
+
name: nameMatch?.[1] || 'AMD GPU',
|
|
187
|
+
vendor: 'AMD',
|
|
188
|
+
vramGB: vramMatch ? Math.round((parseInt(vramMatch[1], 10) / 1024) * 10) / 10 : undefined,
|
|
189
|
+
driverVersion: driverMatch?.[1],
|
|
190
|
+
available: true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Detect AMD GPU on Linux using amdgpu driver (consumer GPUs)
|
|
199
|
+
*/
|
|
200
|
+
static async detectAmdGpuLinux() {
|
|
201
|
+
try {
|
|
202
|
+
// First check if AMD GPU exists via lspci
|
|
203
|
+
const lspciOutput = (0, child_process_1.execSync)('lspci | grep -i "amd\\|ati\\|radeon"', {
|
|
204
|
+
encoding: 'utf8',
|
|
205
|
+
timeout: 5000,
|
|
206
|
+
});
|
|
207
|
+
if (!lspciOutput.trim()) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
// Extract GPU name from lspci
|
|
211
|
+
const lines = lspciOutput.trim().split('\n');
|
|
212
|
+
const firstLine = lines[0];
|
|
213
|
+
const gpuName = firstLine.split(':')[2]?.trim() || 'AMD GPU';
|
|
214
|
+
// Try to get VRAM from /sys/class/drm/
|
|
215
|
+
// Look for card* devices
|
|
216
|
+
const drmCards = (0, child_process_1.execSync)('ls -d /sys/class/drm/card* 2>/dev/null | head -1', {
|
|
217
|
+
encoding: 'utf8',
|
|
218
|
+
timeout: 2000,
|
|
219
|
+
}).trim();
|
|
220
|
+
if (drmCards) {
|
|
221
|
+
// Try to read VRAM size from device
|
|
222
|
+
try {
|
|
223
|
+
// Check if it's an AMD GPU by looking at device path
|
|
224
|
+
const devicePath = (0, child_process_1.execSync)(`readlink -f ${drmCards}`, {
|
|
225
|
+
encoding: 'utf8',
|
|
226
|
+
timeout: 2000,
|
|
227
|
+
}).trim();
|
|
228
|
+
if (devicePath.includes('amdgpu')) {
|
|
229
|
+
// Try to get VRAM from mem_info_vram_total (in bytes)
|
|
230
|
+
try {
|
|
231
|
+
const vramBytes = (0, child_process_1.execSync)(`cat ${drmCards}/device/mem_info_vram_total 2>/dev/null`, {
|
|
232
|
+
encoding: 'utf8',
|
|
233
|
+
timeout: 2000,
|
|
234
|
+
}).trim();
|
|
235
|
+
if (vramBytes && !isNaN(parseInt(vramBytes, 10))) {
|
|
236
|
+
const vramGB = Math.round((parseInt(vramBytes, 10) / (1024 * 1024 * 1024)) * 10) / 10;
|
|
237
|
+
return {
|
|
238
|
+
name: gpuName,
|
|
239
|
+
vendor: 'AMD',
|
|
240
|
+
vramGB,
|
|
241
|
+
available: true,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Try alternative method
|
|
247
|
+
}
|
|
248
|
+
// Alternative: try to get from radeontop or other tools
|
|
249
|
+
if (this.hasCommand('radeontop')) {
|
|
250
|
+
try {
|
|
251
|
+
// radeontop can show VRAM but requires running, so skip for now
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Ignore
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Try to get from lspci -v (may show memory size)
|
|
258
|
+
try {
|
|
259
|
+
const lspciVOutput = (0, child_process_1.execSync)(`lspci -v -s ${firstLine.split(' ')[0]}`, {
|
|
260
|
+
encoding: 'utf8',
|
|
261
|
+
timeout: 3000,
|
|
262
|
+
});
|
|
263
|
+
// Look for memory size in output (format: Memory at ... [size=8G])
|
|
264
|
+
const memoryMatch = lspciVOutput.match(/Memory.*?\[size=(\d+)([KMGT]?)\]/i);
|
|
265
|
+
if (memoryMatch) {
|
|
266
|
+
let size = parseInt(memoryMatch[1], 10);
|
|
267
|
+
const unit = memoryMatch[2].toUpperCase();
|
|
268
|
+
if (unit === 'G') {
|
|
269
|
+
// Already in GB
|
|
270
|
+
}
|
|
271
|
+
else if (unit === 'M') {
|
|
272
|
+
size = size / 1024;
|
|
273
|
+
}
|
|
274
|
+
else if (unit === 'T') {
|
|
275
|
+
size = size * 1024;
|
|
276
|
+
}
|
|
277
|
+
// K is too small, ignore
|
|
278
|
+
if (size >= 1) {
|
|
279
|
+
// Likely VRAM if it's a reasonable size
|
|
280
|
+
return {
|
|
281
|
+
name: gpuName,
|
|
282
|
+
vendor: 'AMD',
|
|
283
|
+
vramGB: Math.round(size * 10) / 10,
|
|
284
|
+
available: true,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Ignore
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
// Ignore errors
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// If we found an AMD GPU but couldn't get VRAM, return basic info
|
|
299
|
+
return {
|
|
300
|
+
name: gpuName,
|
|
301
|
+
vendor: 'AMD',
|
|
302
|
+
available: true,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Detect GPU via lspci (Linux)
|
|
311
|
+
*/
|
|
312
|
+
static async detectGpuViaLspci() {
|
|
313
|
+
try {
|
|
314
|
+
const output = (0, child_process_1.execSync)('lspci | grep -i "vga\\|3d\\|display"', {
|
|
315
|
+
encoding: 'utf8',
|
|
316
|
+
timeout: 5000,
|
|
317
|
+
});
|
|
318
|
+
const lines = output.trim().split('\n');
|
|
319
|
+
if (lines.length === 0) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
const line = lines[0];
|
|
323
|
+
let vendor = 'Unknown';
|
|
324
|
+
let name = 'Unknown';
|
|
325
|
+
if (line.includes('NVIDIA')) {
|
|
326
|
+
vendor = 'NVIDIA';
|
|
327
|
+
name = line.split(':')[2]?.trim() || 'NVIDIA GPU';
|
|
328
|
+
}
|
|
329
|
+
else if (line.includes('AMD') || line.includes('ATI')) {
|
|
330
|
+
vendor = 'AMD';
|
|
331
|
+
name = line.split(':')[2]?.trim() || 'AMD GPU';
|
|
332
|
+
}
|
|
333
|
+
else if (line.includes('Intel')) {
|
|
334
|
+
vendor = 'Intel';
|
|
335
|
+
name = line.split(':')[2]?.trim() || 'Intel GPU';
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
name = line.split(':')[2]?.trim() || 'Unknown GPU';
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
name,
|
|
342
|
+
vendor,
|
|
343
|
+
available: true,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Detect GPU on macOS (Apple Silicon or Intel)
|
|
352
|
+
*/
|
|
353
|
+
static async detectMacOSGpu() {
|
|
354
|
+
try {
|
|
355
|
+
// Use system_profiler to get GPU information
|
|
356
|
+
const output = (0, child_process_1.execSync)('system_profiler SPDisplaysDataType', {
|
|
357
|
+
encoding: 'utf8',
|
|
358
|
+
timeout: 5000,
|
|
359
|
+
});
|
|
360
|
+
// Check if it's Apple Silicon
|
|
361
|
+
const isAppleSilicon = output.includes('Apple') && (output.includes('M1') ||
|
|
362
|
+
output.includes('M2') ||
|
|
363
|
+
output.includes('M3') ||
|
|
364
|
+
output.includes('M4') ||
|
|
365
|
+
output.includes('Apple GPU'));
|
|
366
|
+
// Extract GPU name - try multiple patterns
|
|
367
|
+
let name = 'Apple GPU';
|
|
368
|
+
const chipsetMatch = output.match(/Chipset Model:\s*(.+)/i);
|
|
369
|
+
const modelMatch = output.match(/Model:\s*(.+)/i);
|
|
370
|
+
const deviceMatch = output.match(/Device ID:\s*(.+)/i);
|
|
371
|
+
if (chipsetMatch) {
|
|
372
|
+
name = chipsetMatch[1].trim();
|
|
373
|
+
}
|
|
374
|
+
else if (modelMatch) {
|
|
375
|
+
name = modelMatch[1].trim();
|
|
376
|
+
}
|
|
377
|
+
// Extract VRAM - try multiple patterns for different GPU types
|
|
378
|
+
let vramGB;
|
|
379
|
+
// Pattern 1: "VRAM (Total): 4096 MB" or "VRAM: 4096 MB"
|
|
380
|
+
let vramMatch = output.match(/VRAM\s*\(?Total\)?:\s*(\d+)\s*MB/i) ||
|
|
381
|
+
output.match(/VRAM:\s*(\d+)\s*MB/i);
|
|
382
|
+
if (vramMatch) {
|
|
383
|
+
vramGB = Math.round((parseInt(vramMatch[1], 10) / 1024) * 10) / 10;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
// Pattern 2: "VRAM (Dynamic, Max): 4096 MB" - for AMD GPUs
|
|
387
|
+
vramMatch = output.match(/VRAM\s*\(Dynamic[^)]*\):\s*(\d+)\s*MB/i);
|
|
388
|
+
if (vramMatch) {
|
|
389
|
+
vramGB = Math.round((parseInt(vramMatch[1], 10) / 1024) * 10) / 10;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
// Pattern 3: Look for memory size in different formats
|
|
393
|
+
// Some AMD GPUs show: "Memory: 4096 MB" or "Total Memory: 4096 MB"
|
|
394
|
+
vramMatch = output.match(/(?:Total\s+)?Memory:\s*(\d+)\s*MB/i);
|
|
395
|
+
if (vramMatch) {
|
|
396
|
+
const memMB = parseInt(vramMatch[1], 10);
|
|
397
|
+
// Only consider it VRAM if it's a reasonable size (>= 512MB, <= 64GB)
|
|
398
|
+
if (memMB >= 512 && memMB <= 65536) {
|
|
399
|
+
vramGB = Math.round((memMB / 1024) * 10) / 10;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// For Apple Silicon, VRAM is shared with system RAM
|
|
405
|
+
if (isAppleSilicon && !vramGB) {
|
|
406
|
+
// Apple Silicon uses unified memory - we'll note this in the reasoning
|
|
407
|
+
// but don't set a specific VRAM value as it's shared and dynamically allocated
|
|
408
|
+
}
|
|
409
|
+
// Determine vendor
|
|
410
|
+
let vendor = 'Apple';
|
|
411
|
+
if (isAppleSilicon) {
|
|
412
|
+
// Extract chip model (M1, M2, M3, etc.)
|
|
413
|
+
const chipMatch = output.match(/(M[1-4]\s*(?:Pro|Max|Ultra)?)/i) ||
|
|
414
|
+
name.match(/(M[1-4]\s*(?:Pro|Max|Ultra)?)/i);
|
|
415
|
+
if (chipMatch) {
|
|
416
|
+
vendor = `Apple Silicon (${chipMatch[1]})`;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
vendor = 'Apple Silicon';
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else if (output.includes('AMD') || name.includes('AMD') || name.includes('Radeon')) {
|
|
423
|
+
vendor = 'AMD';
|
|
424
|
+
// For AMD GPUs, try to extract more specific model info
|
|
425
|
+
const radeonMatch = output.match(/Radeon\s+(Pro\s+)?([A-Z0-9\s]+)/i) ||
|
|
426
|
+
name.match(/Radeon\s+(Pro\s+)?([A-Z0-9\s]+)/i);
|
|
427
|
+
if (radeonMatch && !name.includes('Radeon')) {
|
|
428
|
+
name = `AMD Radeon ${radeonMatch[2]?.trim() || name}`;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else if (output.includes('NVIDIA')) {
|
|
432
|
+
vendor = 'NVIDIA';
|
|
433
|
+
}
|
|
434
|
+
else if (output.includes('Intel')) {
|
|
435
|
+
vendor = 'Intel';
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
name,
|
|
439
|
+
vendor,
|
|
440
|
+
vramGB,
|
|
441
|
+
available: true,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
// Fallback: try to detect Apple Silicon via uname
|
|
446
|
+
try {
|
|
447
|
+
const arch = (0, child_process_1.execSync)('uname -m', { encoding: 'utf8', timeout: 2000 }).trim();
|
|
448
|
+
if (arch === 'arm64') {
|
|
449
|
+
// Likely Apple Silicon
|
|
450
|
+
const chipInfo = (0, child_process_1.execSync)('sysctl -n machdep.cpu.brand_string', {
|
|
451
|
+
encoding: 'utf8',
|
|
452
|
+
timeout: 2000,
|
|
453
|
+
}).trim();
|
|
454
|
+
return {
|
|
455
|
+
name: 'Apple GPU',
|
|
456
|
+
vendor: chipInfo.includes('Apple') ? 'Apple Silicon' : 'Apple',
|
|
457
|
+
available: true,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
// Ignore fallback errors
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Detect GPU via WMIC (Windows) - includes VRAM for AMD and NVIDIA
|
|
469
|
+
*/
|
|
470
|
+
static async detectGpuViaWmic() {
|
|
471
|
+
try {
|
|
472
|
+
// Get GPU name and adapter RAM (VRAM)
|
|
473
|
+
const output = (0, child_process_1.execSync)('wmic path win32_VideoController get name,AdapterRAM,DriverVersion', {
|
|
474
|
+
encoding: 'utf8',
|
|
475
|
+
timeout: 5000,
|
|
476
|
+
});
|
|
477
|
+
const lines = output.trim().split('\n').filter((line) => line.trim() && !line.includes('Name'));
|
|
478
|
+
if (lines.length === 0) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
// Parse the first GPU
|
|
482
|
+
const firstLine = lines[0].trim();
|
|
483
|
+
// WMIC output format can vary, try to parse it
|
|
484
|
+
// Format is typically: Name AdapterRAM DriverVersion
|
|
485
|
+
// Try to extract name (usually first field)
|
|
486
|
+
const parts = firstLine.split(/\s{2,}/); // Split on multiple spaces
|
|
487
|
+
let name = parts[0]?.trim() || firstLine;
|
|
488
|
+
let vramBytes;
|
|
489
|
+
let driverVersion;
|
|
490
|
+
// Try to get VRAM from AdapterRAM field
|
|
491
|
+
try {
|
|
492
|
+
const vramOutput = (0, child_process_1.execSync)(`wmic path win32_VideoController where "Name='${name.replace(/'/g, "''")}'" get AdapterRAM /value`, {
|
|
493
|
+
encoding: 'utf8',
|
|
494
|
+
timeout: 3000,
|
|
495
|
+
});
|
|
496
|
+
const vramMatch = vramOutput.match(/AdapterRAM=(\d+)/);
|
|
497
|
+
if (vramMatch) {
|
|
498
|
+
vramBytes = parseInt(vramMatch[1], 10);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
// Try alternative: get all info in one query
|
|
503
|
+
try {
|
|
504
|
+
const fullOutput = (0, child_process_1.execSync)(`wmic path win32_VideoController where "Name='${name.replace(/'/g, "''")}'" get AdapterRAM,DriverVersion /format:list`, {
|
|
505
|
+
encoding: 'utf8',
|
|
506
|
+
timeout: 3000,
|
|
507
|
+
});
|
|
508
|
+
const vramMatch = fullOutput.match(/AdapterRAM=(\d+)/);
|
|
509
|
+
const driverMatch = fullOutput.match(/DriverVersion=(.+)/);
|
|
510
|
+
if (vramMatch) {
|
|
511
|
+
vramBytes = parseInt(vramMatch[1], 10);
|
|
512
|
+
}
|
|
513
|
+
if (driverMatch) {
|
|
514
|
+
driverVersion = driverMatch[1].trim();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
// Ignore
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
let vendor = 'Unknown';
|
|
522
|
+
if (name.includes('NVIDIA')) {
|
|
523
|
+
vendor = 'NVIDIA';
|
|
524
|
+
}
|
|
525
|
+
else if (name.includes('AMD') || name.includes('Radeon')) {
|
|
526
|
+
vendor = 'AMD';
|
|
527
|
+
}
|
|
528
|
+
else if (name.includes('Intel')) {
|
|
529
|
+
vendor = 'Intel';
|
|
530
|
+
}
|
|
531
|
+
const vramGB = vramBytes ? Math.round((vramBytes / (1024 * 1024 * 1024)) * 10) / 10 : undefined;
|
|
532
|
+
return {
|
|
533
|
+
name,
|
|
534
|
+
vendor,
|
|
535
|
+
vramGB,
|
|
536
|
+
driverVersion,
|
|
537
|
+
available: true,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Detect GPU driver support and Docker GPU runtime availability
|
|
546
|
+
*/
|
|
547
|
+
static async detectGpuDriverSupport(gpu) {
|
|
548
|
+
const support = {
|
|
549
|
+
cuda: {
|
|
550
|
+
available: false,
|
|
551
|
+
toolkitAvailable: false,
|
|
552
|
+
},
|
|
553
|
+
rocm: {
|
|
554
|
+
available: false,
|
|
555
|
+
},
|
|
556
|
+
metal: {
|
|
557
|
+
available: false,
|
|
558
|
+
},
|
|
559
|
+
dockerGpuSupport: {
|
|
560
|
+
available: false,
|
|
561
|
+
},
|
|
562
|
+
ollamaGpuReady: false,
|
|
563
|
+
};
|
|
564
|
+
// Detect CUDA (NVIDIA)
|
|
565
|
+
if (this.hasCommand('nvidia-smi')) {
|
|
566
|
+
try {
|
|
567
|
+
const nvidiaOutput = (0, child_process_1.execSync)('nvidia-smi --query-gpu=driver_version,cuda_version --format=csv,noheader', {
|
|
568
|
+
encoding: 'utf8',
|
|
569
|
+
timeout: 3000,
|
|
570
|
+
});
|
|
571
|
+
const lines = nvidiaOutput.trim().split('\n');
|
|
572
|
+
if (lines.length > 0) {
|
|
573
|
+
const parts = lines[0].split(',');
|
|
574
|
+
support.cuda.available = true;
|
|
575
|
+
if (parts[1]) {
|
|
576
|
+
support.cuda.version = parts[1].trim();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
// nvidia-smi available but query failed
|
|
582
|
+
}
|
|
583
|
+
// Check for CUDA toolkit
|
|
584
|
+
if (this.hasCommand('nvcc')) {
|
|
585
|
+
try {
|
|
586
|
+
const nvccOutput = (0, child_process_1.execSync)('nvcc --version', {
|
|
587
|
+
encoding: 'utf8',
|
|
588
|
+
timeout: 2000,
|
|
589
|
+
});
|
|
590
|
+
const versionMatch = nvccOutput.match(/release\s+(\d+\.\d+)/i);
|
|
591
|
+
if (versionMatch) {
|
|
592
|
+
support.cuda.toolkitAvailable = true;
|
|
593
|
+
if (!support.cuda.version) {
|
|
594
|
+
support.cuda.version = versionMatch[1];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
// nvcc available but version check failed
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// Detect ROCm (AMD)
|
|
604
|
+
if (this.hasCommand('rocm-smi')) {
|
|
605
|
+
try {
|
|
606
|
+
const rocmOutput = (0, child_process_1.execSync)('rocm-smi --version', {
|
|
607
|
+
encoding: 'utf8',
|
|
608
|
+
timeout: 3000,
|
|
609
|
+
});
|
|
610
|
+
support.rocm.available = true;
|
|
611
|
+
const versionMatch = rocmOutput.match(/version\s+(\d+\.\d+\.\d+)/i);
|
|
612
|
+
if (versionMatch) {
|
|
613
|
+
support.rocm.version = versionMatch[1];
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
// rocm-smi available but version check failed
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// Detect Metal (macOS)
|
|
621
|
+
if (process.platform === 'darwin') {
|
|
622
|
+
try {
|
|
623
|
+
// Check if Metal is available (it's built into macOS)
|
|
624
|
+
const metalCheck = (0, child_process_1.execSync)('system_profiler SPDisplaysDataType | grep -i "metal"', {
|
|
625
|
+
encoding: 'utf8',
|
|
626
|
+
timeout: 2000,
|
|
627
|
+
});
|
|
628
|
+
support.metal.available = true;
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
// Try alternative check
|
|
632
|
+
try {
|
|
633
|
+
// Metal is available on all modern macOS systems
|
|
634
|
+
const osVersion = (0, child_process_1.execSync)('sw_vers -productVersion', {
|
|
635
|
+
encoding: 'utf8',
|
|
636
|
+
timeout: 2000,
|
|
637
|
+
});
|
|
638
|
+
const majorVersion = parseInt(osVersion.split('.')[0], 10);
|
|
639
|
+
// Metal has been available since macOS 10.11
|
|
640
|
+
support.metal.available = majorVersion >= 10;
|
|
641
|
+
}
|
|
642
|
+
catch {
|
|
643
|
+
support.metal.available = false;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
// Detect Docker GPU support
|
|
648
|
+
if (this.hasCommand('docker')) {
|
|
649
|
+
try {
|
|
650
|
+
// Check for NVIDIA Container Runtime
|
|
651
|
+
const dockerInfo = (0, child_process_1.execSync)('docker info 2>/dev/null', {
|
|
652
|
+
encoding: 'utf8',
|
|
653
|
+
timeout: 3000,
|
|
654
|
+
});
|
|
655
|
+
if (dockerInfo.includes('nvidia') || dockerInfo.includes('gpu')) {
|
|
656
|
+
support.dockerGpuSupport.available = true;
|
|
657
|
+
}
|
|
658
|
+
// Check for nvidia-container-runtime
|
|
659
|
+
if (this.hasCommand('nvidia-container-runtime')) {
|
|
660
|
+
support.dockerGpuSupport.nvidiaContainerRuntime = true;
|
|
661
|
+
support.dockerGpuSupport.runtime = 'nvidia';
|
|
662
|
+
support.dockerGpuSupport.available = true;
|
|
663
|
+
}
|
|
664
|
+
// Check for nvidia-docker (legacy)
|
|
665
|
+
if (this.hasCommand('nvidia-docker')) {
|
|
666
|
+
support.dockerGpuSupport.nvidiaDocker = true;
|
|
667
|
+
support.dockerGpuSupport.runtime = 'nvidia';
|
|
668
|
+
support.dockerGpuSupport.available = true;
|
|
669
|
+
}
|
|
670
|
+
// Check Docker daemon configuration for GPU runtimes
|
|
671
|
+
try {
|
|
672
|
+
const dockerVersion = (0, child_process_1.execSync)('docker version --format "{{.Server.Version}}"', {
|
|
673
|
+
encoding: 'utf8',
|
|
674
|
+
timeout: 2000,
|
|
675
|
+
});
|
|
676
|
+
// Try to test GPU access in Docker
|
|
677
|
+
if (support.cuda.available) {
|
|
678
|
+
try {
|
|
679
|
+
// Check if nvidia runtime is available
|
|
680
|
+
const runtimes = (0, child_process_1.execSync)('docker info --format "{{.Runtimes}}"', {
|
|
681
|
+
encoding: 'utf8',
|
|
682
|
+
timeout: 2000,
|
|
683
|
+
});
|
|
684
|
+
if (runtimes.includes('nvidia') || runtimes.includes('gpu')) {
|
|
685
|
+
support.dockerGpuSupport.available = true;
|
|
686
|
+
support.dockerGpuSupport.runtime = 'nvidia';
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
catch {
|
|
690
|
+
// Ignore
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (support.rocm.available) {
|
|
694
|
+
try {
|
|
695
|
+
const runtimes = (0, child_process_1.execSync)('docker info --format "{{.Runtimes}}"', {
|
|
696
|
+
encoding: 'utf8',
|
|
697
|
+
timeout: 2000,
|
|
698
|
+
});
|
|
699
|
+
if (runtimes.includes('rocm')) {
|
|
700
|
+
support.dockerGpuSupport.available = true;
|
|
701
|
+
support.dockerGpuSupport.runtime = 'rocm';
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
// Ignore
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
// Docker not running or can't query
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
// Docker not available or not running
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
// Determine Ollama GPU readiness
|
|
718
|
+
if (gpu) {
|
|
719
|
+
if (gpu.vendor === 'NVIDIA' || gpu.vendor.includes('NVIDIA')) {
|
|
720
|
+
if (support.cuda.available && support.dockerGpuSupport.available) {
|
|
721
|
+
support.ollamaGpuReady = true;
|
|
722
|
+
support.ollamaGpuReason = 'NVIDIA GPU with CUDA and Docker GPU support available';
|
|
723
|
+
}
|
|
724
|
+
else if (support.cuda.available) {
|
|
725
|
+
support.ollamaGpuReady = true;
|
|
726
|
+
support.ollamaGpuReason = 'NVIDIA GPU with CUDA available (Docker GPU support not detected)';
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
support.ollamaGpuReady = false;
|
|
730
|
+
support.ollamaGpuReason = 'NVIDIA GPU detected but CUDA drivers not available';
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
else if (gpu.vendor === 'AMD' || gpu.vendor.includes('AMD')) {
|
|
734
|
+
if (support.rocm.available && support.dockerGpuSupport.available) {
|
|
735
|
+
support.ollamaGpuReady = true;
|
|
736
|
+
support.ollamaGpuReason = 'AMD GPU with ROCm and Docker GPU support available';
|
|
737
|
+
}
|
|
738
|
+
else if (support.rocm.available) {
|
|
739
|
+
support.ollamaGpuReady = true;
|
|
740
|
+
support.ollamaGpuReason = 'AMD GPU with ROCm available (Docker GPU support not detected)';
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
support.ollamaGpuReady = false;
|
|
744
|
+
support.ollamaGpuReason = 'AMD GPU detected but ROCm drivers not available';
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
else if (gpu.vendor.includes('Apple Silicon') || gpu.vendor.includes('Apple')) {
|
|
748
|
+
if (support.metal.available) {
|
|
749
|
+
support.ollamaGpuReady = true;
|
|
750
|
+
support.ollamaGpuReason = 'Apple Silicon GPU with Metal support available';
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
support.ollamaGpuReady = false;
|
|
754
|
+
support.ollamaGpuReason = 'Apple Silicon GPU detected but Metal support not available';
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
support.ollamaGpuReady = false;
|
|
759
|
+
support.ollamaGpuReason = `GPU detected (${gpu.vendor}) but no compatible drivers found`;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
support.ollamaGpuReady = false;
|
|
764
|
+
support.ollamaGpuReason = 'No GPU detected - Ollama will run in CPU-only mode';
|
|
765
|
+
}
|
|
766
|
+
return support;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Check if a command is available
|
|
770
|
+
*/
|
|
771
|
+
static hasCommand(command) {
|
|
772
|
+
try {
|
|
773
|
+
(0, child_process_1.execSync)(`which ${command}`, { encoding: 'utf8', timeout: 1000, stdio: 'ignore' });
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
try {
|
|
778
|
+
// Windows fallback
|
|
779
|
+
(0, child_process_1.execSync)(`where ${command}`, { encoding: 'utf8', timeout: 1000, stdio: 'ignore' });
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
catch {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Recommend LLM models based on system capabilities
|
|
789
|
+
*/
|
|
790
|
+
static recommendLLMModels(cpu, memory, gpu) {
|
|
791
|
+
const recommendations = [];
|
|
792
|
+
// Model definitions with requirements
|
|
793
|
+
const models = [
|
|
794
|
+
{
|
|
795
|
+
modelName: 'Llama 3.2 1B',
|
|
796
|
+
size: '1B',
|
|
797
|
+
parameters: '1 billion',
|
|
798
|
+
minRamGB: 2,
|
|
799
|
+
recommendedRamGB: 4,
|
|
800
|
+
minVramGB: undefined,
|
|
801
|
+
recommendedVramGB: undefined,
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
modelName: 'Llama 3.2 3B',
|
|
805
|
+
size: '3B',
|
|
806
|
+
parameters: '3 billion',
|
|
807
|
+
minRamGB: 4,
|
|
808
|
+
recommendedRamGB: 8,
|
|
809
|
+
minVramGB: 4,
|
|
810
|
+
recommendedVramGB: 6,
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
modelName: 'Llama 3.1 8B',
|
|
814
|
+
size: '8B',
|
|
815
|
+
parameters: '8 billion',
|
|
816
|
+
minRamGB: 8,
|
|
817
|
+
recommendedRamGB: 16,
|
|
818
|
+
minVramGB: 6,
|
|
819
|
+
recommendedVramGB: 8,
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
modelName: 'Llama 3.1 70B',
|
|
823
|
+
size: '70B',
|
|
824
|
+
parameters: '70 billion',
|
|
825
|
+
minRamGB: 40,
|
|
826
|
+
recommendedRamGB: 64,
|
|
827
|
+
minVramGB: 40,
|
|
828
|
+
recommendedVramGB: 48,
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
modelName: 'Mistral 7B',
|
|
832
|
+
size: '7B',
|
|
833
|
+
parameters: '7 billion',
|
|
834
|
+
minRamGB: 8,
|
|
835
|
+
recommendedRamGB: 16,
|
|
836
|
+
minVramGB: 6,
|
|
837
|
+
recommendedVramGB: 8,
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
modelName: 'Phi-3 Mini (3.8B)',
|
|
841
|
+
size: '3.8B',
|
|
842
|
+
parameters: '3.8 billion',
|
|
843
|
+
minRamGB: 4,
|
|
844
|
+
recommendedRamGB: 8,
|
|
845
|
+
minVramGB: 4,
|
|
846
|
+
recommendedVramGB: 6,
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
modelName: 'Qwen2.5 0.5B',
|
|
850
|
+
size: '0.5B',
|
|
851
|
+
parameters: '0.5 billion',
|
|
852
|
+
minRamGB: 2,
|
|
853
|
+
recommendedRamGB: 4,
|
|
854
|
+
minVramGB: undefined,
|
|
855
|
+
recommendedVramGB: undefined,
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
modelName: 'Qwen2.5 7B',
|
|
859
|
+
size: '7B',
|
|
860
|
+
parameters: '7 billion',
|
|
861
|
+
minRamGB: 8,
|
|
862
|
+
recommendedRamGB: 16,
|
|
863
|
+
minVramGB: 6,
|
|
864
|
+
recommendedVramGB: 8,
|
|
865
|
+
},
|
|
866
|
+
// DeepSeek-R1 models (https://ollama.com/library/deepseek-r1)
|
|
867
|
+
{
|
|
868
|
+
modelName: 'DeepSeek-R1 1.5B',
|
|
869
|
+
size: '1.5B',
|
|
870
|
+
parameters: '1.5 billion',
|
|
871
|
+
minRamGB: 2,
|
|
872
|
+
recommendedRamGB: 4,
|
|
873
|
+
minVramGB: undefined,
|
|
874
|
+
recommendedVramGB: undefined,
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
modelName: 'DeepSeek-R1 7B',
|
|
878
|
+
size: '7B',
|
|
879
|
+
parameters: '7 billion',
|
|
880
|
+
minRamGB: 8,
|
|
881
|
+
recommendedRamGB: 16,
|
|
882
|
+
minVramGB: 6,
|
|
883
|
+
recommendedVramGB: 8,
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
modelName: 'DeepSeek-R1 8B',
|
|
887
|
+
size: '8B',
|
|
888
|
+
parameters: '8 billion',
|
|
889
|
+
minRamGB: 8,
|
|
890
|
+
recommendedRamGB: 16,
|
|
891
|
+
minVramGB: 6,
|
|
892
|
+
recommendedVramGB: 8,
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
modelName: 'DeepSeek-R1 14B',
|
|
896
|
+
size: '14B',
|
|
897
|
+
parameters: '14 billion',
|
|
898
|
+
minRamGB: 16,
|
|
899
|
+
recommendedRamGB: 32,
|
|
900
|
+
minVramGB: 10,
|
|
901
|
+
recommendedVramGB: 14,
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
modelName: 'DeepSeek-R1 32B',
|
|
905
|
+
size: '32B',
|
|
906
|
+
parameters: '32 billion',
|
|
907
|
+
minRamGB: 32,
|
|
908
|
+
recommendedRamGB: 48,
|
|
909
|
+
minVramGB: 20,
|
|
910
|
+
recommendedVramGB: 32,
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
modelName: 'DeepSeek-R1 70B',
|
|
914
|
+
size: '70B',
|
|
915
|
+
parameters: '70 billion',
|
|
916
|
+
minRamGB: 40,
|
|
917
|
+
recommendedRamGB: 64,
|
|
918
|
+
minVramGB: 40,
|
|
919
|
+
recommendedVramGB: 48,
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
modelName: 'DeepSeek-R1 671B',
|
|
923
|
+
size: '671B',
|
|
924
|
+
parameters: '671 billion',
|
|
925
|
+
minRamGB: 400,
|
|
926
|
+
recommendedRamGB: 512,
|
|
927
|
+
minVramGB: 400,
|
|
928
|
+
recommendedVramGB: 512,
|
|
929
|
+
},
|
|
930
|
+
];
|
|
931
|
+
for (const model of models) {
|
|
932
|
+
const suitability = this.calculateSuitability(model, cpu, memory, gpu);
|
|
933
|
+
const reasoning = this.generateReasoning(model, cpu, memory, gpu, suitability);
|
|
934
|
+
recommendations.push({
|
|
935
|
+
modelName: model.modelName,
|
|
936
|
+
size: model.size,
|
|
937
|
+
parameters: model.parameters,
|
|
938
|
+
minRamGB: model.minRamGB,
|
|
939
|
+
minVramGB: model.minVramGB,
|
|
940
|
+
recommendedRamGB: model.recommendedRamGB,
|
|
941
|
+
recommendedVramGB: model.recommendedVramGB,
|
|
942
|
+
suitability,
|
|
943
|
+
reasoning,
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
// Sort by suitability (excellent first, then good, etc.)
|
|
947
|
+
const suitabilityOrder = { excellent: 0, good: 1, marginal: 2, insufficient: 3 };
|
|
948
|
+
return recommendations.sort((a, b) => suitabilityOrder[a.suitability] - suitabilityOrder[b.suitability]);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Calculate suitability for a model
|
|
952
|
+
*/
|
|
953
|
+
static calculateSuitability(model, cpu, memory, gpu) {
|
|
954
|
+
// Check RAM
|
|
955
|
+
const hasMinRam = memory.totalGB >= model.minRamGB;
|
|
956
|
+
const hasRecommendedRam = memory.totalGB >= model.recommendedRamGB;
|
|
957
|
+
if (!hasMinRam) {
|
|
958
|
+
return 'insufficient';
|
|
959
|
+
}
|
|
960
|
+
// Check if it's Apple Silicon (unified memory architecture)
|
|
961
|
+
const isAppleSilicon = gpu?.vendor?.includes('Apple Silicon') || false;
|
|
962
|
+
// Check VRAM if model requires it and GPU is available
|
|
963
|
+
if (model.minVramGB && gpu) {
|
|
964
|
+
if (isAppleSilicon) {
|
|
965
|
+
// Apple Silicon uses unified memory - total RAM is available for GPU
|
|
966
|
+
// We need RAM for both system and model, so check if total RAM covers both
|
|
967
|
+
const totalRamNeeded = model.minRamGB + model.minVramGB;
|
|
968
|
+
const recommendedRamNeeded = model.recommendedRamGB + (model.recommendedVramGB || 0);
|
|
969
|
+
const hasMinTotalRam = memory.totalGB >= totalRamNeeded;
|
|
970
|
+
const hasRecommendedTotalRam = memory.totalGB >= recommendedRamNeeded;
|
|
971
|
+
if (!hasMinTotalRam) {
|
|
972
|
+
return 'insufficient';
|
|
973
|
+
}
|
|
974
|
+
if (hasRecommendedTotalRam && cpu.cores >= 4) {
|
|
975
|
+
return 'excellent';
|
|
976
|
+
}
|
|
977
|
+
if (hasMinTotalRam && cpu.cores >= 2) {
|
|
978
|
+
return 'good';
|
|
979
|
+
}
|
|
980
|
+
return 'marginal';
|
|
981
|
+
}
|
|
982
|
+
else if (gpu.vramGB) {
|
|
983
|
+
// Traditional GPU with dedicated VRAM
|
|
984
|
+
const hasMinVram = gpu.vramGB >= model.minVramGB;
|
|
985
|
+
const hasRecommendedVram = model.recommendedVramGB
|
|
986
|
+
? gpu.vramGB >= model.recommendedVramGB
|
|
987
|
+
: true;
|
|
988
|
+
if (!hasMinVram) {
|
|
989
|
+
return 'insufficient';
|
|
990
|
+
}
|
|
991
|
+
if (hasRecommendedRam && hasRecommendedVram) {
|
|
992
|
+
return 'excellent';
|
|
993
|
+
}
|
|
994
|
+
if (hasMinRam && hasMinVram) {
|
|
995
|
+
return 'good';
|
|
996
|
+
}
|
|
997
|
+
return 'marginal';
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
// If no GPU requirement or no GPU available, check RAM and CPU
|
|
1001
|
+
if (hasRecommendedRam && cpu.cores >= 4) {
|
|
1002
|
+
return 'excellent';
|
|
1003
|
+
}
|
|
1004
|
+
if (hasMinRam && cpu.cores >= 2) {
|
|
1005
|
+
return 'good';
|
|
1006
|
+
}
|
|
1007
|
+
return 'marginal';
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Generate reasoning for recommendation
|
|
1011
|
+
*/
|
|
1012
|
+
static generateReasoning(model, cpu, memory, gpu, suitability) {
|
|
1013
|
+
const reasons = [];
|
|
1014
|
+
if (suitability === 'insufficient') {
|
|
1015
|
+
const isAppleSilicon = gpu?.vendor?.includes('Apple Silicon') || false;
|
|
1016
|
+
if (memory.totalGB < model.minRamGB) {
|
|
1017
|
+
reasons.push(`Insufficient RAM: ${memory.totalGB.toFixed(1)}GB available, ${model.minRamGB}GB minimum required`);
|
|
1018
|
+
}
|
|
1019
|
+
if (model.minVramGB) {
|
|
1020
|
+
if (isAppleSilicon) {
|
|
1021
|
+
// Apple Silicon uses unified memory
|
|
1022
|
+
const totalRamNeeded = model.minRamGB + model.minVramGB;
|
|
1023
|
+
if (memory.totalGB < totalRamNeeded) {
|
|
1024
|
+
reasons.push(`Insufficient unified memory: ${memory.totalGB.toFixed(1)}GB available, ${totalRamNeeded}GB minimum required (RAM + VRAM)`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
else if (!gpu || !gpu.vramGB || gpu.vramGB < model.minVramGB) {
|
|
1028
|
+
reasons.push(`Insufficient VRAM: ${gpu?.vramGB?.toFixed(1) || 'No GPU'}GB available, ${model.minVramGB}GB minimum required`);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return reasons.join('. ');
|
|
1032
|
+
}
|
|
1033
|
+
const isAppleSilicon = gpu?.vendor?.includes('Apple Silicon') || false;
|
|
1034
|
+
if (isAppleSilicon && model.minVramGB) {
|
|
1035
|
+
// Apple Silicon uses unified memory
|
|
1036
|
+
const totalRamNeeded = model.minRamGB + model.minVramGB;
|
|
1037
|
+
const recommendedRamNeeded = model.recommendedRamGB + (model.recommendedVramGB || 0);
|
|
1038
|
+
if (memory.totalGB >= recommendedRamNeeded) {
|
|
1039
|
+
reasons.push(`Unified Memory: ${memory.totalGB.toFixed(1)}GB (recommended: ${recommendedRamNeeded}GB for RAM + VRAM)`);
|
|
1040
|
+
}
|
|
1041
|
+
else if (memory.totalGB >= totalRamNeeded) {
|
|
1042
|
+
reasons.push(`Unified Memory: ${memory.totalGB.toFixed(1)}GB (minimum: ${totalRamNeeded}GB for RAM + VRAM)`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
if (memory.totalGB >= model.recommendedRamGB) {
|
|
1047
|
+
reasons.push(`RAM: ${memory.totalGB.toFixed(1)}GB (recommended: ${model.recommendedRamGB}GB)`);
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
reasons.push(`RAM: ${memory.totalGB.toFixed(1)}GB (minimum: ${model.minRamGB}GB)`);
|
|
1051
|
+
}
|
|
1052
|
+
if (model.recommendedVramGB && gpu?.vramGB) {
|
|
1053
|
+
if (gpu.vramGB >= model.recommendedVramGB) {
|
|
1054
|
+
reasons.push(`VRAM: ${gpu.vramGB.toFixed(1)}GB (recommended: ${model.recommendedVramGB}GB)`);
|
|
1055
|
+
}
|
|
1056
|
+
else if (gpu.vramGB >= (model.minVramGB || 0)) {
|
|
1057
|
+
reasons.push(`VRAM: ${gpu.vramGB.toFixed(1)}GB (minimum: ${model.minVramGB}GB)`);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
else if (model.minVramGB && !gpu) {
|
|
1061
|
+
reasons.push('No GPU detected (CPU-only mode possible but slower)');
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (cpu.cores >= 4) {
|
|
1065
|
+
reasons.push(`CPU: ${cpu.cores} cores`);
|
|
1066
|
+
}
|
|
1067
|
+
return reasons.join(', ');
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Convert bytes to GB
|
|
1071
|
+
*/
|
|
1072
|
+
static bytesToGB(bytes) {
|
|
1073
|
+
return Math.round((bytes / (1024 * 1024 * 1024)) * 10) / 10;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Display system capabilities
|
|
1077
|
+
*/
|
|
1078
|
+
static displayCapabilities(capabilities) {
|
|
1079
|
+
console.log(chalk_1.default.blue.bold('\n🖥️ System Capabilities:\n'));
|
|
1080
|
+
// CPU
|
|
1081
|
+
console.log(chalk_1.default.cyan.bold('CPU:'));
|
|
1082
|
+
console.log(chalk_1.default.white(` Model: ${capabilities.cpu.model}`));
|
|
1083
|
+
console.log(chalk_1.default.white(` Cores: ${capabilities.cpu.cores}`));
|
|
1084
|
+
console.log(chalk_1.default.white(` Architecture: ${capabilities.cpu.architecture}`));
|
|
1085
|
+
console.log(chalk_1.default.white(` Speed: ${capabilities.cpu.speed.toFixed(2)} GHz\n`));
|
|
1086
|
+
// Memory
|
|
1087
|
+
console.log(chalk_1.default.cyan.bold('Memory:'));
|
|
1088
|
+
console.log(chalk_1.default.white(` Total: ${capabilities.memory.totalGB.toFixed(1)} GB`));
|
|
1089
|
+
console.log(chalk_1.default.white(` Used: ${capabilities.memory.usedGB.toFixed(1)} GB`));
|
|
1090
|
+
console.log(chalk_1.default.white(` Free: ${capabilities.memory.freeGB.toFixed(1)} GB\n`));
|
|
1091
|
+
// GPU
|
|
1092
|
+
if (capabilities.gpu) {
|
|
1093
|
+
console.log(chalk_1.default.cyan.bold('GPU:'));
|
|
1094
|
+
console.log(chalk_1.default.white(` Name: ${capabilities.gpu.name}`));
|
|
1095
|
+
console.log(chalk_1.default.white(` Vendor: ${capabilities.gpu.vendor}`));
|
|
1096
|
+
if (capabilities.gpu.vramGB) {
|
|
1097
|
+
console.log(chalk_1.default.white(` VRAM: ${capabilities.gpu.vramGB.toFixed(1)} GB`));
|
|
1098
|
+
}
|
|
1099
|
+
if (capabilities.gpu.driverVersion) {
|
|
1100
|
+
console.log(chalk_1.default.white(` Driver: ${capabilities.gpu.driverVersion}`));
|
|
1101
|
+
}
|
|
1102
|
+
console.log('');
|
|
1103
|
+
}
|
|
1104
|
+
else {
|
|
1105
|
+
console.log(chalk_1.default.yellow('GPU: No dedicated GPU detected\n'));
|
|
1106
|
+
}
|
|
1107
|
+
// GPU Driver Support & Ollama Readiness
|
|
1108
|
+
console.log(chalk_1.default.cyan.bold('GPU Driver Support:\n'));
|
|
1109
|
+
const support = capabilities.gpuDriverSupport;
|
|
1110
|
+
// CUDA (NVIDIA)
|
|
1111
|
+
if (support.cuda.available) {
|
|
1112
|
+
console.log(chalk_1.default.green(' ✓ CUDA: Available'));
|
|
1113
|
+
if (support.cuda.version) {
|
|
1114
|
+
console.log(chalk_1.default.gray(` Version: ${support.cuda.version}`));
|
|
1115
|
+
}
|
|
1116
|
+
if (support.cuda.toolkitAvailable) {
|
|
1117
|
+
console.log(chalk_1.default.gray(' Toolkit: Available'));
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
else if (capabilities.gpu?.vendor?.includes('NVIDIA')) {
|
|
1121
|
+
console.log(chalk_1.default.red(' ✗ CUDA: Not available'));
|
|
1122
|
+
console.log(chalk_1.default.gray(' Install NVIDIA drivers and CUDA toolkit'));
|
|
1123
|
+
}
|
|
1124
|
+
// ROCm (AMD)
|
|
1125
|
+
if (support.rocm.available) {
|
|
1126
|
+
console.log(chalk_1.default.green(' ✓ ROCm: Available'));
|
|
1127
|
+
if (support.rocm.version) {
|
|
1128
|
+
console.log(chalk_1.default.gray(` Version: ${support.rocm.version}`));
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
else if (capabilities.gpu?.vendor?.includes('AMD')) {
|
|
1132
|
+
console.log(chalk_1.default.yellow(' ⚠ ROCm: Not available'));
|
|
1133
|
+
console.log(chalk_1.default.gray(' Install ROCm drivers for AMD GPU acceleration'));
|
|
1134
|
+
}
|
|
1135
|
+
// Metal (macOS)
|
|
1136
|
+
if (support.metal.available) {
|
|
1137
|
+
console.log(chalk_1.default.green(' ✓ Metal: Available (macOS)'));
|
|
1138
|
+
}
|
|
1139
|
+
// Docker GPU Support
|
|
1140
|
+
console.log('');
|
|
1141
|
+
if (support.dockerGpuSupport.available) {
|
|
1142
|
+
console.log(chalk_1.default.green(' ✓ Docker GPU Support: Available'));
|
|
1143
|
+
if (support.dockerGpuSupport.runtime) {
|
|
1144
|
+
console.log(chalk_1.default.gray(` Runtime: ${support.dockerGpuSupport.runtime}`));
|
|
1145
|
+
}
|
|
1146
|
+
if (support.dockerGpuSupport.nvidiaContainerRuntime) {
|
|
1147
|
+
console.log(chalk_1.default.gray(' NVIDIA Container Runtime: Installed'));
|
|
1148
|
+
}
|
|
1149
|
+
if (support.dockerGpuSupport.nvidiaDocker) {
|
|
1150
|
+
console.log(chalk_1.default.gray(' nvidia-docker: Installed (legacy)'));
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
console.log(chalk_1.default.yellow(' ⚠ Docker GPU Support: Not detected'));
|
|
1155
|
+
if (capabilities.gpu) {
|
|
1156
|
+
console.log(chalk_1.default.gray(' Install nvidia-container-runtime or configure Docker for GPU access'));
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
console.log(chalk_1.default.gray(' No GPU detected - Docker GPU support not needed'));
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
// Ollama GPU Readiness
|
|
1163
|
+
console.log('');
|
|
1164
|
+
if (support.ollamaGpuReady) {
|
|
1165
|
+
console.log(chalk_1.default.green.bold(' ✓ Ollama GPU Ready: Yes'));
|
|
1166
|
+
if (support.ollamaGpuReason) {
|
|
1167
|
+
console.log(chalk_1.default.gray(` ${support.ollamaGpuReason}`));
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
console.log(chalk_1.default.yellow.bold(' ⚠ Ollama GPU Ready: No'));
|
|
1172
|
+
if (support.ollamaGpuReason) {
|
|
1173
|
+
console.log(chalk_1.default.gray(` ${support.ollamaGpuReason}`));
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
console.log('');
|
|
1177
|
+
// Overall assessment
|
|
1178
|
+
if (capabilities.hasEnoughResources) {
|
|
1179
|
+
console.log(chalk_1.default.green.bold('✓ System has sufficient resources for local LLM\n'));
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
console.log(chalk_1.default.yellow.bold('⚠ System may have limited resources for local LLM\n'));
|
|
1183
|
+
}
|
|
1184
|
+
// Recommendations
|
|
1185
|
+
console.log(chalk_1.default.blue.bold('📊 Recommended LLM Models:\n'));
|
|
1186
|
+
const excellent = capabilities.recommendedModels.filter((m) => m.suitability === 'excellent');
|
|
1187
|
+
const good = capabilities.recommendedModels.filter((m) => m.suitability === 'good');
|
|
1188
|
+
const marginal = capabilities.recommendedModels.filter((m) => m.suitability === 'marginal');
|
|
1189
|
+
const insufficient = capabilities.recommendedModels.filter((m) => m.suitability === 'insufficient');
|
|
1190
|
+
if (excellent.length > 0) {
|
|
1191
|
+
console.log(chalk_1.default.green.bold('Excellent Fit:'));
|
|
1192
|
+
excellent.forEach((model) => {
|
|
1193
|
+
console.log(chalk_1.default.green(` ✓ ${model.modelName} (${model.size})`));
|
|
1194
|
+
console.log(chalk_1.default.gray(` ${model.reasoning}`));
|
|
1195
|
+
});
|
|
1196
|
+
console.log('');
|
|
1197
|
+
}
|
|
1198
|
+
if (good.length > 0) {
|
|
1199
|
+
console.log(chalk_1.default.cyan.bold('Good Fit:'));
|
|
1200
|
+
good.forEach((model) => {
|
|
1201
|
+
console.log(chalk_1.default.cyan(` • ${model.modelName} (${model.size})`));
|
|
1202
|
+
console.log(chalk_1.default.gray(` ${model.reasoning}`));
|
|
1203
|
+
});
|
|
1204
|
+
console.log('');
|
|
1205
|
+
}
|
|
1206
|
+
if (marginal.length > 0) {
|
|
1207
|
+
console.log(chalk_1.default.yellow.bold('Marginal (may work but not recommended):'));
|
|
1208
|
+
marginal.forEach((model) => {
|
|
1209
|
+
console.log(chalk_1.default.yellow(` ⚠ ${model.modelName} (${model.size})`));
|
|
1210
|
+
console.log(chalk_1.default.gray(` ${model.reasoning}`));
|
|
1211
|
+
});
|
|
1212
|
+
console.log('');
|
|
1213
|
+
}
|
|
1214
|
+
if (insufficient.length > 0) {
|
|
1215
|
+
console.log(chalk_1.default.red.bold('Insufficient Resources:'));
|
|
1216
|
+
insufficient.forEach((model) => {
|
|
1217
|
+
console.log(chalk_1.default.red(` ✗ ${model.modelName} (${model.size})`));
|
|
1218
|
+
console.log(chalk_1.default.gray(` ${model.reasoning}`));
|
|
1219
|
+
});
|
|
1220
|
+
console.log('');
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Get summary of capabilities
|
|
1225
|
+
*/
|
|
1226
|
+
static getCapabilitySummary(capabilities) {
|
|
1227
|
+
const parts = [];
|
|
1228
|
+
parts.push(`${capabilities.cpu.cores} cores`);
|
|
1229
|
+
parts.push(`${capabilities.memory.totalGB.toFixed(1)}GB RAM`);
|
|
1230
|
+
if (capabilities.gpu) {
|
|
1231
|
+
parts.push(`${capabilities.gpu.vendor} GPU`);
|
|
1232
|
+
if (capabilities.gpu.vramGB) {
|
|
1233
|
+
parts.push(`${capabilities.gpu.vramGB.toFixed(1)}GB VRAM`);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
parts.push('No GPU');
|
|
1238
|
+
}
|
|
1239
|
+
const suitableModels = capabilities.recommendedModels.filter((m) => m.suitability === 'excellent' || m.suitability === 'good').length;
|
|
1240
|
+
return `${parts.join(', ')} - ${suitableModels} suitable LLM models`;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
exports.SystemCapabilityDetector = SystemCapabilityDetector;
|
|
1244
|
+
//# sourceMappingURL=SystemCapabilityDetector.js.map
|