llm-checker 3.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/LICENSE +21 -0
- package/README.md +418 -0
- package/analyzer/compatibility.js +584 -0
- package/analyzer/performance.js +505 -0
- package/bin/CLAUDE.md +12 -0
- package/bin/enhanced_cli.js +3118 -0
- package/bin/test-deterministic.js +41 -0
- package/package.json +96 -0
- package/src/CLAUDE.md +12 -0
- package/src/ai/intelligent-selector.js +615 -0
- package/src/ai/model-selector.js +312 -0
- package/src/ai/multi-objective-selector.js +820 -0
- package/src/commands/check.js +58 -0
- package/src/data/CLAUDE.md +11 -0
- package/src/data/model-database.js +637 -0
- package/src/data/sync-manager.js +279 -0
- package/src/hardware/CLAUDE.md +12 -0
- package/src/hardware/backends/CLAUDE.md +11 -0
- package/src/hardware/backends/apple-silicon.js +318 -0
- package/src/hardware/backends/cpu-detector.js +490 -0
- package/src/hardware/backends/cuda-detector.js +417 -0
- package/src/hardware/backends/intel-detector.js +436 -0
- package/src/hardware/backends/rocm-detector.js +440 -0
- package/src/hardware/detector.js +573 -0
- package/src/hardware/pc-optimizer.js +635 -0
- package/src/hardware/specs.js +286 -0
- package/src/hardware/unified-detector.js +442 -0
- package/src/index.js +2289 -0
- package/src/models/CLAUDE.md +17 -0
- package/src/models/ai-check-selector.js +806 -0
- package/src/models/catalog.json +426 -0
- package/src/models/deterministic-selector.js +1145 -0
- package/src/models/expanded_database.js +1142 -0
- package/src/models/intelligent-selector.js +532 -0
- package/src/models/requirements.js +310 -0
- package/src/models/scoring-config.js +57 -0
- package/src/models/scoring-engine.js +715 -0
- package/src/ollama/.cache/README.md +33 -0
- package/src/ollama/CLAUDE.md +24 -0
- package/src/ollama/client.js +438 -0
- package/src/ollama/enhanced-client.js +113 -0
- package/src/ollama/enhanced-scraper.js +634 -0
- package/src/ollama/manager.js +357 -0
- package/src/ollama/native-scraper.js +776 -0
- package/src/plugins/CLAUDE.md +11 -0
- package/src/plugins/examples/custom_model_plugin.js +87 -0
- package/src/plugins/index.js +295 -0
- package/src/utils/CLAUDE.md +11 -0
- package/src/utils/config.js +359 -0
- package/src/utils/formatter.js +315 -0
- package/src/utils/logger.js +272 -0
- package/src/utils/model-classifier.js +167 -0
- package/src/utils/verbose-progress.js +266 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
const si = require('systeminformation');
|
|
2
|
+
|
|
3
|
+
class HardwareDetector {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.cache = null;
|
|
6
|
+
this.cacheExpiry = 5 * 60 * 1000;
|
|
7
|
+
this.cacheTime = 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async getSystemInfo(forceFresh = false) {
|
|
11
|
+
|
|
12
|
+
if (!forceFresh && this.cache && (Date.now() - this.cacheTime < this.cacheExpiry)) {
|
|
13
|
+
return this.cache;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const [cpu, memory, graphics, system, osInfo] = await Promise.all([
|
|
18
|
+
si.cpu(),
|
|
19
|
+
si.mem(),
|
|
20
|
+
si.graphics(),
|
|
21
|
+
si.system(),
|
|
22
|
+
si.osInfo()
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const systemInfo = {
|
|
26
|
+
cpu: this.processCPUInfo(cpu),
|
|
27
|
+
memory: this.processMemoryInfo(memory),
|
|
28
|
+
gpu: this.processGPUInfo(graphics),
|
|
29
|
+
system: this.processSystemInfo(system),
|
|
30
|
+
os: this.processOSInfo(osInfo),
|
|
31
|
+
timestamp: Date.now()
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
this.cache = systemInfo;
|
|
35
|
+
this.cacheTime = Date.now();
|
|
36
|
+
|
|
37
|
+
return systemInfo;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
throw new Error(`Failed to detect hardware: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
processCPUInfo(cpu) {
|
|
44
|
+
return {
|
|
45
|
+
brand: cpu.brand || 'Unknown',
|
|
46
|
+
manufacturer: cpu.manufacturer || 'Unknown',
|
|
47
|
+
family: cpu.family || 'Unknown',
|
|
48
|
+
model: cpu.model || 'Unknown',
|
|
49
|
+
speed: cpu.speed || 0,
|
|
50
|
+
speedMax: cpu.speedMax || cpu.speed || 0,
|
|
51
|
+
cores: cpu.cores || 1,
|
|
52
|
+
physicalCores: cpu.physicalCores || cpu.cores || 1,
|
|
53
|
+
processors: cpu.processors || 1,
|
|
54
|
+
cache: {
|
|
55
|
+
l1d: cpu.cache?.l1d || 0,
|
|
56
|
+
l1i: cpu.cache?.l1i || 0,
|
|
57
|
+
l2: cpu.cache?.l2 || 0,
|
|
58
|
+
l3: cpu.cache?.l3 || 0
|
|
59
|
+
},
|
|
60
|
+
architecture: this.detectArchitecture(cpu),
|
|
61
|
+
score: this.calculateCPUScore(cpu)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
processMemoryInfo(memory) {
|
|
66
|
+
const totalGB = Math.round(memory.total / (1024 ** 3));
|
|
67
|
+
const freeGB = Math.round(memory.free / (1024 ** 3));
|
|
68
|
+
const usedGB = totalGB - freeGB;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
total: totalGB,
|
|
72
|
+
free: freeGB,
|
|
73
|
+
used: usedGB,
|
|
74
|
+
available: Math.round(memory.available / (1024 ** 3)),
|
|
75
|
+
usagePercent: Math.round((usedGB / totalGB) * 100),
|
|
76
|
+
swapTotal: Math.round(memory.swaptotal / (1024 ** 3)),
|
|
77
|
+
swapUsed: Math.round(memory.swapused / (1024 ** 3)),
|
|
78
|
+
score: this.calculateMemoryScore(totalGB, freeGB)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
processGPUInfo(graphics) {
|
|
83
|
+
const controllers = graphics.controllers || [];
|
|
84
|
+
const displays = graphics.displays || [];
|
|
85
|
+
|
|
86
|
+
// Debug logging to help diagnose GPU detection issues
|
|
87
|
+
if (process.env.DEBUG_GPU) {
|
|
88
|
+
console.log('GPU Detection Debug:', JSON.stringify(controllers, null, 2));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// Filter out invalid/virtualized GPUs first
|
|
93
|
+
const validGPUs = controllers.filter(gpu => {
|
|
94
|
+
const model = (gpu.model || '').toLowerCase();
|
|
95
|
+
const vendor = (gpu.vendor || '').toLowerCase();
|
|
96
|
+
|
|
97
|
+
// Skip GPUs with empty/invalid data (like virtualized GPUs)
|
|
98
|
+
if (!model || !vendor || model === 'unknown' || vendor === '') {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Skip very generic/placeholder entries
|
|
103
|
+
if (model.includes('standard vga') || model.includes('microsoft basic')) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Find all dedicated GPUs from valid GPUs
|
|
111
|
+
const dedicatedGPUs = validGPUs.filter(gpu => {
|
|
112
|
+
const model = (gpu.model || '').toLowerCase();
|
|
113
|
+
const isDedicated = !this.isIntegratedGPU(gpu.model) && (
|
|
114
|
+
gpu.vram > 0 || // Has dedicated VRAM
|
|
115
|
+
model.includes('rtx') || // NVIDIA RTX series
|
|
116
|
+
model.includes('gtx') || // NVIDIA GTX series
|
|
117
|
+
model.includes('radeon rx') || // AMD RX series
|
|
118
|
+
model.includes('tesla') || // NVIDIA Tesla
|
|
119
|
+
model.includes('quadro') || // NVIDIA Quadro
|
|
120
|
+
model.includes('geforce') // NVIDIA GeForce
|
|
121
|
+
);
|
|
122
|
+
return isDedicated;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Find integrated GPUs from valid GPUs
|
|
126
|
+
const integratedGPUs = validGPUs.filter(gpu =>
|
|
127
|
+
this.isIntegratedGPU(gpu.model)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Select the best GPU using smart selection logic
|
|
131
|
+
const primaryGPU = this.selectBestGPU(dedicatedGPUs, integratedGPUs, validGPUs);
|
|
132
|
+
|
|
133
|
+
if (!primaryGPU) {
|
|
134
|
+
return {
|
|
135
|
+
model: 'No GPU detected',
|
|
136
|
+
vendor: 'Unknown',
|
|
137
|
+
vram: 0,
|
|
138
|
+
dedicated: false,
|
|
139
|
+
score: 0
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Enhance model detection using device ID when model is generic or missing
|
|
144
|
+
let enhancedModel = primaryGPU.model || 'Unknown GPU';
|
|
145
|
+
if (primaryGPU.deviceId && (
|
|
146
|
+
!primaryGPU.model ||
|
|
147
|
+
primaryGPU.model === 'Unknown' ||
|
|
148
|
+
primaryGPU.model.includes('NVIDIA Corporation Device')
|
|
149
|
+
)) {
|
|
150
|
+
enhancedModel = this.getGPUModelFromDeviceId(primaryGPU.deviceId) || enhancedModel;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Enhanced VRAM detection using the new normalizeVRAM function
|
|
154
|
+
let vram = this.normalizeVRAM(primaryGPU.vram || 0);
|
|
155
|
+
|
|
156
|
+
// If VRAM is still 0, try to estimate based on model or handle unified memory
|
|
157
|
+
if (vram === 0 && primaryGPU.model) {
|
|
158
|
+
const modelLower = primaryGPU.model.toLowerCase();
|
|
159
|
+
if (modelLower.includes('apple') || modelLower.includes('unified')) {
|
|
160
|
+
// Apple Silicon uses unified memory - return 0 to indicate this
|
|
161
|
+
vram = 0;
|
|
162
|
+
} else {
|
|
163
|
+
vram = this.estimateVRAMFromModel(primaryGPU.model);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Calculate total VRAM from all dedicated GPUs (for multi-GPU setups)
|
|
168
|
+
let totalDedicatedVRAM = 0;
|
|
169
|
+
let gpuCount = 0;
|
|
170
|
+
|
|
171
|
+
dedicatedGPUs.forEach(gpu => {
|
|
172
|
+
const gpuVram = this.normalizeVRAM(gpu.vram || 0) || this.estimateVRAMFromModel(gpu.model);
|
|
173
|
+
if (gpuVram > 0) {
|
|
174
|
+
totalDedicatedVRAM += gpuVram;
|
|
175
|
+
gpuCount++;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// If we have multiple dedicated GPUs, use the combined VRAM
|
|
180
|
+
const effectiveVRAM = gpuCount > 1 ? totalDedicatedVRAM : vram;
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
model: enhancedModel,
|
|
184
|
+
vendor: primaryGPU.vendor || 'Unknown',
|
|
185
|
+
vram: effectiveVRAM,
|
|
186
|
+
vramPerGPU: vram, // VRAM of primary GPU for reference
|
|
187
|
+
vramDynamic: primaryGPU.vramDynamic || false,
|
|
188
|
+
dedicated: !this.isIntegratedGPU(enhancedModel),
|
|
189
|
+
driverVersion: primaryGPU.driverVersion || 'Unknown',
|
|
190
|
+
gpuCount: gpuCount > 0 ? gpuCount : (dedicatedGPUs.length > 0 ? dedicatedGPUs.length : 1),
|
|
191
|
+
isMultiGPU: gpuCount > 1,
|
|
192
|
+
all: controllers.map(gpu => ({
|
|
193
|
+
model: gpu.model,
|
|
194
|
+
vram: this.normalizeVRAM(gpu.vram || 0),
|
|
195
|
+
vendor: gpu.vendor
|
|
196
|
+
})),
|
|
197
|
+
displays: displays.length,
|
|
198
|
+
score: this.calculateGPUScore(primaryGPU)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
processSystemInfo(system) {
|
|
203
|
+
return {
|
|
204
|
+
manufacturer: system.manufacturer || 'Unknown',
|
|
205
|
+
model: system.model || 'Unknown',
|
|
206
|
+
version: system.version || 'Unknown',
|
|
207
|
+
serial: system.serial || 'Unknown',
|
|
208
|
+
uuid: system.uuid || 'Unknown',
|
|
209
|
+
sku: system.sku || 'Unknown'
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
processOSInfo(osInfo) {
|
|
214
|
+
return {
|
|
215
|
+
platform: osInfo.platform || process.platform,
|
|
216
|
+
distro: osInfo.distro || 'Unknown',
|
|
217
|
+
release: osInfo.release || 'Unknown',
|
|
218
|
+
codename: osInfo.codename || 'Unknown',
|
|
219
|
+
kernel: osInfo.kernel || 'Unknown',
|
|
220
|
+
arch: osInfo.arch || process.arch,
|
|
221
|
+
hostname: osInfo.hostname || 'Unknown',
|
|
222
|
+
logofile: osInfo.logofile || ''
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
detectArchitecture(cpu) {
|
|
227
|
+
const brand = (cpu.brand || '').toLowerCase();
|
|
228
|
+
const model = (cpu.model || '').toLowerCase();
|
|
229
|
+
const manufacturer = (cpu.manufacturer || '').toLowerCase();
|
|
230
|
+
|
|
231
|
+
if (manufacturer.includes('apple') || brand.includes('apple') || brand.includes('m1') || brand.includes('m2') || brand.includes('m3') || brand.includes('m4')) {
|
|
232
|
+
return 'Apple Silicon';
|
|
233
|
+
} else if (brand.includes('intel')) {
|
|
234
|
+
return 'x86_64';
|
|
235
|
+
} else if (brand.includes('amd')) {
|
|
236
|
+
return 'x86_64';
|
|
237
|
+
} else if (process.arch === 'arm64') {
|
|
238
|
+
return 'ARM64';
|
|
239
|
+
} else {
|
|
240
|
+
return process.arch || 'Unknown';
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
isIntegratedGPU(model) {
|
|
245
|
+
if (!model) return false;
|
|
246
|
+
const modelLower = model.toLowerCase();
|
|
247
|
+
// Check if GPU is integrated (on-chip or shared memory, not discrete)
|
|
248
|
+
// Note: && has higher precedence than ||, each line is grouped with ()
|
|
249
|
+
return (modelLower.includes('intel') && !modelLower.includes('arc')) ||
|
|
250
|
+
(modelLower.includes('amd') && modelLower.includes('graphics') && !modelLower.includes(' rx ')) ||
|
|
251
|
+
(modelLower.includes('radeon') && modelLower.includes('graphics')) ||
|
|
252
|
+
modelLower.includes('iris') ||
|
|
253
|
+
modelLower.includes('uhd') ||
|
|
254
|
+
modelLower.includes('hd graphics') ||
|
|
255
|
+
modelLower.includes('apple');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
getGPUModelFromDeviceId(deviceId) {
|
|
259
|
+
if (!deviceId) return null;
|
|
260
|
+
|
|
261
|
+
// Normalize device ID (remove 0x prefix if present and convert to lowercase)
|
|
262
|
+
const normalizedId = deviceId.toLowerCase().replace('0x', '');
|
|
263
|
+
|
|
264
|
+
// NVIDIA RTX 50 series device IDs
|
|
265
|
+
const deviceIdMap = {
|
|
266
|
+
'2d04': 'NVIDIA GeForce RTX 5060 Ti',
|
|
267
|
+
'2d05': 'NVIDIA GeForce RTX 5060',
|
|
268
|
+
'2d06': 'NVIDIA GeForce RTX 5070',
|
|
269
|
+
'2d07': 'NVIDIA GeForce RTX 5070 Ti',
|
|
270
|
+
'2d08': 'NVIDIA GeForce RTX 5080',
|
|
271
|
+
'2d09': 'NVIDIA GeForce RTX 5090',
|
|
272
|
+
|
|
273
|
+
// NVIDIA RTX 40 series device IDs
|
|
274
|
+
'2684': 'NVIDIA GeForce RTX 4090',
|
|
275
|
+
'2685': 'NVIDIA GeForce RTX 4080',
|
|
276
|
+
'2786': 'NVIDIA GeForce RTX 4070 Ti',
|
|
277
|
+
'2787': 'NVIDIA GeForce RTX 4070',
|
|
278
|
+
'27a0': 'NVIDIA GeForce RTX 4060 Ti',
|
|
279
|
+
'27a1': 'NVIDIA GeForce RTX 4060',
|
|
280
|
+
|
|
281
|
+
// NVIDIA RTX 30 series device IDs
|
|
282
|
+
'2204': 'NVIDIA GeForce RTX 3090',
|
|
283
|
+
'2206': 'NVIDIA GeForce RTX 3080',
|
|
284
|
+
'2484': 'NVIDIA GeForce RTX 3070',
|
|
285
|
+
'2487': 'NVIDIA GeForce RTX 3060 Ti',
|
|
286
|
+
'2504': 'NVIDIA GeForce RTX 3060'
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
return deviceIdMap[normalizedId] || null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
estimateVRAMFromModel(model) {
|
|
293
|
+
if (!model) return 0;
|
|
294
|
+
const modelLower = model.toLowerCase();
|
|
295
|
+
|
|
296
|
+
// NVIDIA RTX 50 series
|
|
297
|
+
if (modelLower.includes('rtx 5090')) return 32;
|
|
298
|
+
if (modelLower.includes('rtx 5080')) return 16;
|
|
299
|
+
if (modelLower.includes('rtx 5070 ti')) return 16;
|
|
300
|
+
if (modelLower.includes('rtx 5070')) return 12;
|
|
301
|
+
if (modelLower.includes('rtx 5060 ti')) return 16;
|
|
302
|
+
if (modelLower.includes('rtx 5060')) return 8;
|
|
303
|
+
|
|
304
|
+
// NVIDIA RTX 40 series
|
|
305
|
+
if (modelLower.includes('rtx 4090')) return 24;
|
|
306
|
+
if (modelLower.includes('rtx 4080')) return 16;
|
|
307
|
+
if (modelLower.includes('rtx 4070 ti')) return 12;
|
|
308
|
+
if (modelLower.includes('rtx 4070')) return 12;
|
|
309
|
+
if (modelLower.includes('rtx 4060 ti')) return 16;
|
|
310
|
+
if (modelLower.includes('rtx 4060')) return 8;
|
|
311
|
+
|
|
312
|
+
// NVIDIA RTX 30 series
|
|
313
|
+
if (modelLower.includes('rtx 3090')) return 24;
|
|
314
|
+
if (modelLower.includes('rtx 3080 ti')) return 12;
|
|
315
|
+
if (modelLower.includes('rtx 3080')) return 10;
|
|
316
|
+
if (modelLower.includes('rtx 3070')) return 8;
|
|
317
|
+
if (modelLower.includes('rtx 3060 ti')) return 8;
|
|
318
|
+
if (modelLower.includes('rtx 3060')) return 12;
|
|
319
|
+
|
|
320
|
+
// AMD RX 7000 series
|
|
321
|
+
if (modelLower.includes('rx 7900')) return 24;
|
|
322
|
+
if (modelLower.includes('rx 7800')) return 16;
|
|
323
|
+
if (modelLower.includes('rx 7700')) return 12;
|
|
324
|
+
if (modelLower.includes('rx 7600')) return 8;
|
|
325
|
+
|
|
326
|
+
// Generic estimates
|
|
327
|
+
if (modelLower.includes('rtx')) return 8; // Default for RTX
|
|
328
|
+
if (modelLower.includes('gtx')) return 4; // Default for GTX
|
|
329
|
+
if (modelLower.includes('rx ')) return 8; // Default for AMD RX
|
|
330
|
+
|
|
331
|
+
return 0; // Unknown or integrated
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
calculateCPUScore(cpu) {
|
|
335
|
+
let score = 0;
|
|
336
|
+
|
|
337
|
+
// Base score por número de cores
|
|
338
|
+
score += (cpu.cores || 1) * 5;
|
|
339
|
+
score += (cpu.physicalCores || cpu.cores || 1) * 3;
|
|
340
|
+
|
|
341
|
+
// Score por velocidad
|
|
342
|
+
const speed = cpu.speedMax || cpu.speed || 0;
|
|
343
|
+
score += speed * 10;
|
|
344
|
+
|
|
345
|
+
// Bonus por arquitectura moderna
|
|
346
|
+
const brand = (cpu.brand || '').toLowerCase();
|
|
347
|
+
if (brand.includes('apple m')) {
|
|
348
|
+
score += 20; // Apple Silicon bonus
|
|
349
|
+
} else if (brand.includes('intel') && speed > 3.0) {
|
|
350
|
+
score += 15;
|
|
351
|
+
} else if (brand.includes('amd') && speed > 3.0) {
|
|
352
|
+
score += 15;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return Math.min(Math.round(score), 100);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
calculateMemoryScore(totalGB, freeGB) {
|
|
359
|
+
let score = 0;
|
|
360
|
+
|
|
361
|
+
// Score basado en RAM total
|
|
362
|
+
if (totalGB >= 64) score += 40;
|
|
363
|
+
else if (totalGB >= 32) score += 35;
|
|
364
|
+
else if (totalGB >= 16) score += 25;
|
|
365
|
+
else if (totalGB >= 8) score += 15;
|
|
366
|
+
else score += totalGB * 2;
|
|
367
|
+
|
|
368
|
+
// Score basado en RAM disponible
|
|
369
|
+
const freePercent = (freeGB / totalGB) * 100;
|
|
370
|
+
if (freePercent > 50) score += 20;
|
|
371
|
+
else if (freePercent > 30) score += 15;
|
|
372
|
+
else if (freePercent > 20) score += 10;
|
|
373
|
+
else score += 5;
|
|
374
|
+
|
|
375
|
+
return Math.min(Math.round(score), 100);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
calculateGPUScore(gpu) {
|
|
379
|
+
if (!gpu || !gpu.model) return 0;
|
|
380
|
+
|
|
381
|
+
let score = 0;
|
|
382
|
+
const model = gpu.model.toLowerCase();
|
|
383
|
+
const vram = gpu.vram || 0;
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
score += vram * 8;
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
if (!this.isIntegratedGPU(gpu.model)) {
|
|
390
|
+
score += 20;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Bonus por marcas/modelos específicos
|
|
394
|
+
if (model.includes('rtx 5090')) score += 30;
|
|
395
|
+
else if (model.includes('rtx 5080')) score += 27;
|
|
396
|
+
else if (model.includes('rtx 5070')) score += 24;
|
|
397
|
+
else if (model.includes('rtx 5060')) score += 21;
|
|
398
|
+
else if (model.includes('rtx 4090')) score += 25;
|
|
399
|
+
else if (model.includes('rtx 4080')) score += 22;
|
|
400
|
+
else if (model.includes('rtx 4070')) score += 20;
|
|
401
|
+
else if (model.includes('rtx 30')) score += 18;
|
|
402
|
+
else if (model.includes('rtx 20')) score += 15;
|
|
403
|
+
else if (model.includes('gtx 16')) score += 12;
|
|
404
|
+
else if (model.includes('apple m')) score += 15;
|
|
405
|
+
|
|
406
|
+
return Math.min(Math.round(score), 100);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Select the best GPU from multiple available GPUs
|
|
411
|
+
* Prioritizes: 1) Dedicated GPUs by VRAM, 2) Model tier, 3) Integrated GPUs
|
|
412
|
+
*/
|
|
413
|
+
selectBestGPU(dedicatedGPUs, integratedGPUs, validGPUs) {
|
|
414
|
+
// If we have dedicated GPUs, choose the best one
|
|
415
|
+
if (dedicatedGPUs.length > 0) {
|
|
416
|
+
// Sort dedicated GPUs by a combination of VRAM and model tier
|
|
417
|
+
return dedicatedGPUs.sort((a, b) => {
|
|
418
|
+
// First priority: VRAM amount
|
|
419
|
+
const vramA = this.normalizeVRAM(a.vram || 0);
|
|
420
|
+
const vramB = this.normalizeVRAM(b.vram || 0);
|
|
421
|
+
|
|
422
|
+
if (vramA !== vramB) {
|
|
423
|
+
return vramB - vramA; // Higher VRAM first
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Second priority: GPU tier (RTX 50xx > RTX 40xx > RTX 30xx, etc.)
|
|
427
|
+
const tierA = this.getGPUTier(a.model || '');
|
|
428
|
+
const tierB = this.getGPUTier(b.model || '');
|
|
429
|
+
|
|
430
|
+
if (tierA !== tierB) {
|
|
431
|
+
return tierB - tierA; // Higher tier first
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Third priority: Vendor preference (NVIDIA > AMD > Intel)
|
|
435
|
+
const vendorA = this.getVendorPriority(a.vendor || '');
|
|
436
|
+
const vendorB = this.getVendorPriority(b.vendor || '');
|
|
437
|
+
|
|
438
|
+
return vendorB - vendorA;
|
|
439
|
+
})[0];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// If no dedicated GPUs, use the best integrated GPU
|
|
443
|
+
if (integratedGPUs.length > 0) {
|
|
444
|
+
return integratedGPUs.sort((a, b) => {
|
|
445
|
+
const tierA = this.getGPUTier(a.model || '');
|
|
446
|
+
const tierB = this.getGPUTier(b.model || '');
|
|
447
|
+
return tierB - tierA;
|
|
448
|
+
})[0];
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Fallback to any valid GPU (should rarely happen)
|
|
452
|
+
return validGPUs.length > 0 ? validGPUs[0] : null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Normalize VRAM values (handle different units and wrong totals)
|
|
457
|
+
*/
|
|
458
|
+
normalizeVRAM(vram) {
|
|
459
|
+
if (!vram || vram <= 0) return 0;
|
|
460
|
+
|
|
461
|
+
let vramValue = vram;
|
|
462
|
+
|
|
463
|
+
// Handle VRAM in bytes (some systems report this way)
|
|
464
|
+
if (vramValue > 100000) {
|
|
465
|
+
vramValue = Math.round(vramValue / (1024 * 1024)); // Convert bytes to MB
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Now determine if we have MB or GB values
|
|
469
|
+
if (vramValue >= 1024) {
|
|
470
|
+
// Values >= 1024 are likely MB, convert to GB
|
|
471
|
+
vramValue = Math.round(vramValue / 1024);
|
|
472
|
+
} else if (vramValue >= 512 && vramValue < 1024) {
|
|
473
|
+
// 512-1023 MB, round to 1GB
|
|
474
|
+
vramValue = 1;
|
|
475
|
+
} else if (vramValue > 80) {
|
|
476
|
+
// Values between 80-511 are likely incorrect MB values, treat as MB
|
|
477
|
+
vramValue = Math.round(vramValue / 1024) || 1;
|
|
478
|
+
} else if (vramValue >= 1 && vramValue <= 80) {
|
|
479
|
+
// Values 1-80 are likely already in GB, keep as is
|
|
480
|
+
vramValue = vramValue;
|
|
481
|
+
} else {
|
|
482
|
+
// Values < 1 round to 0
|
|
483
|
+
vramValue = 0;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return vramValue;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get GPU tier score for prioritization
|
|
491
|
+
*/
|
|
492
|
+
getGPUTier(model) {
|
|
493
|
+
const modelLower = model.toLowerCase();
|
|
494
|
+
|
|
495
|
+
// NVIDIA RTX series
|
|
496
|
+
if (modelLower.includes('rtx 50')) return 100;
|
|
497
|
+
if (modelLower.includes('rtx 4090')) return 95;
|
|
498
|
+
if (modelLower.includes('rtx 40')) return 90;
|
|
499
|
+
if (modelLower.includes('rtx 3090')) return 85;
|
|
500
|
+
if (modelLower.includes('rtx 30')) return 80;
|
|
501
|
+
if (modelLower.includes('rtx 20')) return 70;
|
|
502
|
+
if (modelLower.includes('gtx 16')) return 60;
|
|
503
|
+
if (modelLower.includes('gtx 10')) return 50;
|
|
504
|
+
|
|
505
|
+
// NVIDIA Professional
|
|
506
|
+
if (modelLower.includes('a100')) return 98;
|
|
507
|
+
if (modelLower.includes('h100')) return 99;
|
|
508
|
+
if (modelLower.includes('tesla')) return 75;
|
|
509
|
+
if (modelLower.includes('quadro')) return 65;
|
|
510
|
+
|
|
511
|
+
// AMD
|
|
512
|
+
if (modelLower.includes('rx 7900')) return 85;
|
|
513
|
+
if (modelLower.includes('rx 7800')) return 80;
|
|
514
|
+
if (modelLower.includes('rx 7700')) return 75;
|
|
515
|
+
if (modelLower.includes('rx 6900')) return 70;
|
|
516
|
+
if (modelLower.includes('rx 6800')) return 65;
|
|
517
|
+
|
|
518
|
+
// Intel
|
|
519
|
+
if (modelLower.includes('arc a7')) return 55;
|
|
520
|
+
if (modelLower.includes('arc a5')) return 45;
|
|
521
|
+
|
|
522
|
+
// Apple Silicon
|
|
523
|
+
if (modelLower.includes('apple') || modelLower.includes('m1') ||
|
|
524
|
+
modelLower.includes('m2') || modelLower.includes('m3') ||
|
|
525
|
+
modelLower.includes('m4')) return 80;
|
|
526
|
+
|
|
527
|
+
return 10; // Default for unknown
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Get vendor priority score
|
|
532
|
+
*/
|
|
533
|
+
getVendorPriority(vendor) {
|
|
534
|
+
const vendorLower = vendor.toLowerCase();
|
|
535
|
+
if (vendorLower.includes('nvidia')) return 3;
|
|
536
|
+
if (vendorLower.includes('amd') || vendorLower.includes('ati')) return 2;
|
|
537
|
+
if (vendorLower.includes('intel')) return 1;
|
|
538
|
+
if (vendorLower.includes('apple')) return 3;
|
|
539
|
+
return 0;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async runQuickBenchmark() {
|
|
543
|
+
|
|
544
|
+
const start = process.hrtime.bigint();
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
let cpuResult = 0;
|
|
548
|
+
for (let i = 0; i < 1000000; i++) {
|
|
549
|
+
cpuResult += Math.sqrt(i);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const end = process.hrtime.bigint();
|
|
553
|
+
const cpuTime = Number(end - start) / 1000000; // ms
|
|
554
|
+
|
|
555
|
+
const memStart = process.hrtime.bigint();
|
|
556
|
+
const largeArray = new Array(1000000).fill(0).map((_, i) => i);
|
|
557
|
+
largeArray.sort((a, b) => b - a);
|
|
558
|
+
const memEnd = process.hrtime.bigint();
|
|
559
|
+
const memTime = Number(memEnd - memStart) / 1000000;
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
cpu: Math.max(0, Math.min(100, 100 - (cpuTime / 100))),
|
|
563
|
+
memory: Math.max(0, Math.min(100, 100 - (memTime / 50))),
|
|
564
|
+
overall: Math.round((
|
|
565
|
+
Math.max(0, Math.min(100, 100 - (cpuTime / 100))) +
|
|
566
|
+
Math.max(0, Math.min(100, 100 - (memTime / 50)))
|
|
567
|
+
) / 2)
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
module.exports = HardwareDetector;
|