llm-checker 3.2.0 → 3.2.2

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/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "llm-checker",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "Intelligent CLI tool with AI-powered model selection that analyzes your hardware and recommends optimal LLM models for your system",
5
5
  "bin": {
6
- "llm-checker": "bin/enhanced_cli.js",
7
- "ollama-checker": "bin/enhanced_cli.js",
6
+ "llm-checker": "bin/cli.js",
7
+ "ollama-checker": "bin/cli.js",
8
8
  "llm-checker-mcp": "bin/mcp-server.mjs"
9
9
  },
10
10
  "main": "src/index.js",
@@ -13,6 +13,13 @@
13
13
  "test:gpu": "node tests/gpu-detection/multi-gpu.test.js",
14
14
  "test:platform": "node tests/platform-tests/cross-platform.test.js",
15
15
  "test:ui": "node tests/ui-tests/interface.test.js",
16
+ "test:runtime": "node tests/runtime-specdec-tests.js",
17
+ "test:deterministic-pool": "node tests/deterministic-model-pool-check.js",
18
+ "test:policy": "node tests/policy-commands.test.js",
19
+ "test:policy-cli": "node tests/policy-cli-enforcement.js",
20
+ "test:policy-engine": "node tests/policy-engine.test.js",
21
+ "test:policy-e2e": "node tests/policy-e2e-integration.test.js",
22
+ "test:hardware-detector": "node tests/hardware-detector-regression.js",
16
23
  "test:all": "node tests/run-all-tests.js",
17
24
  "build": "echo 'No build needed'",
18
25
  "dev": "node bin/enhanced_cli.js",
@@ -36,6 +43,7 @@
36
43
  "ora": "^5.4.1",
37
44
  "systeminformation": "^5.21.0",
38
45
  "table": "^6.8.1",
46
+ "yaml": "^2.8.1",
39
47
  "zod": "^3.23.0"
40
48
  },
41
49
  "optionalDependencies": {
@@ -387,6 +387,9 @@ class MultiObjectiveSelector {
387
387
  // 2) Memory bandwidth (20%) - simplified estimation
388
388
  let memBandwidthGBs = 50; // fallback
389
389
  const gpu = gpuModel.toLowerCase();
390
+ if (gpu.includes('gb10') || gpu.includes('grace blackwell') || gpu.includes('dgx spark')) memBandwidthGBs = 1000;
391
+ else if (gpu.includes('h100')) memBandwidthGBs = 3000;
392
+ else if (gpu.includes('a100')) memBandwidthGBs = 2039;
390
393
  if (gpu.includes('m4 pro')) memBandwidthGBs = 273;
391
394
  else if (gpu.includes('m4')) memBandwidthGBs = 120;
392
395
  else if (gpu.includes('rtx 4090')) memBandwidthGBs = 1008;
@@ -398,7 +401,10 @@ class MultiObjectiveSelector {
398
401
 
399
402
  // 3) Compute (20%) - simplified estimation
400
403
  let compute = 0;
401
- if (gpu.includes('m4 pro')) compute = clamp(28 / 80); // Match main algorithm
404
+ if (gpu.includes('gb10') || gpu.includes('grace blackwell') || gpu.includes('dgx spark')) compute = clamp(180 / 80);
405
+ else if (gpu.includes('h100')) compute = clamp(320 / 80);
406
+ else if (gpu.includes('a100')) compute = clamp(250 / 80);
407
+ else if (gpu.includes('m4 pro')) compute = clamp(28 / 80); // Match main algorithm
402
408
  else if (gpu.includes('m4')) compute = clamp(15 / 80);
403
409
  else if (gpu.includes('rtx 4090')) compute = clamp(165 / 80);
404
410
  else if (gpu.includes('rtx 4080')) compute = clamp(121 / 80);
@@ -448,6 +454,10 @@ class MultiObjectiveSelector {
448
454
 
449
455
  // Special flagship GPU detection by model name
450
456
  if (gpuModel.toLowerCase().includes('rtx 50') ||
457
+ gpuModel.toLowerCase().includes('gb10') ||
458
+ gpuModel.toLowerCase().includes('grace blackwell') ||
459
+ gpuModel.toLowerCase().includes('dgx spark') ||
460
+ gpuModel.toLowerCase().includes('blackwell') ||
451
461
  gpuModel.toLowerCase().includes('h100') ||
452
462
  gpuModel.toLowerCase().includes('a100')) {
453
463
  tier = 'flagship';
@@ -599,7 +609,11 @@ class MultiObjectiveSelector {
599
609
 
600
610
  // NVIDIA GPU optimizations
601
611
  if (gpu.includes('nvidia') || gpu.includes('geforce') || gpu.includes('rtx') || gpu.includes('gtx')) {
602
- if (gpu.includes('rtx 50')) {
612
+ if (gpu.includes('gb10') || gpu.includes('grace blackwell') || gpu.includes('dgx spark')) {
613
+ specs.offloadCapacity = Math.min(ramGB * 0.6, 32);
614
+ specs.memoryEfficiency = 0.96;
615
+ specs.backendOptimization = 1.25;
616
+ } else if (gpu.includes('rtx 50')) {
603
617
  // RTX 50xx series - flagship tier with massive VRAM + excellent offload
604
618
  specs.offloadCapacity = Math.min(ramGB * 0.5, 24);
605
619
  specs.memoryEfficiency = 0.95;
@@ -732,7 +746,15 @@ class MultiObjectiveSelector {
732
746
  // GPU-based calculation (dedicated GPU only)
733
747
  if (vramGB > 0 && !gpuModel.toLowerCase().includes('iris') && !gpuModel.toLowerCase().includes('integrated')) {
734
748
  let gpuTPS = 20; // Conservative GPU baseline
735
- if (gpuModel.toLowerCase().includes('rtx 50')) {
749
+ if (gpuModel.toLowerCase().includes('gb10') ||
750
+ gpuModel.toLowerCase().includes('grace blackwell') ||
751
+ gpuModel.toLowerCase().includes('dgx spark')) {
752
+ gpuTPS = 85; // GB10 / Grace Blackwell class
753
+ } else if (gpuModel.toLowerCase().includes('h100')) {
754
+ gpuTPS = 120;
755
+ } else if (gpuModel.toLowerCase().includes('a100')) {
756
+ gpuTPS = 95;
757
+ } else if (gpuModel.toLowerCase().includes('rtx 50')) {
736
758
  gpuTPS = 60; // RTX 50 series - more realistic
737
759
  } else if (gpuModel.toLowerCase().includes('rtx 40')) {
738
760
  gpuTPS = 45; // RTX 40 series
@@ -740,6 +762,8 @@ class MultiObjectiveSelector {
740
762
  gpuTPS = 35; // RTX 30 series
741
763
  } else if (gpuModel.toLowerCase().includes('rtx 20')) {
742
764
  gpuTPS = 25; // RTX 20 series
765
+ } else if (gpuModel.toLowerCase().includes('p100')) {
766
+ gpuTPS = 32; // Tesla P100 class
743
767
  } else if (vramGB >= 8) {
744
768
  gpuTPS = 30; // Other high-end GPUs
745
769
  } else if (vramGB >= 4) {
@@ -817,4 +841,4 @@ class MultiObjectiveSelector {
817
841
  }
818
842
  }
819
843
 
820
- module.exports = MultiObjectiveSelector;
844
+ module.exports = MultiObjectiveSelector;
@@ -209,8 +209,34 @@ class CUDADetector {
209
209
  architecture: 'Unknown'
210
210
  };
211
211
 
212
+ // NVIDIA GB10 / Grace Blackwell (DGX Spark)
213
+ if (nameLower.includes('gb10') || nameLower.includes('grace blackwell') ||
214
+ nameLower.includes('dgx spark') || nameLower.includes('blackwell')) {
215
+ capabilities.tensorCores = true;
216
+ capabilities.bf16 = true;
217
+ capabilities.fp8 = true;
218
+ capabilities.computeCapability = '10.0';
219
+ capabilities.architecture = 'Grace Blackwell';
220
+ }
221
+ // H100 (Hopper)
222
+ else if (nameLower.includes('h100') || nameLower.includes('h200')) {
223
+ capabilities.tensorCores = true;
224
+ capabilities.bf16 = true;
225
+ capabilities.fp8 = true;
226
+ capabilities.nvlink = true;
227
+ capabilities.computeCapability = '9.0';
228
+ capabilities.architecture = 'Hopper';
229
+ }
230
+ // Tesla P100 (Pascal)
231
+ else if (nameLower.includes('p100') || nameLower.includes('tesla p100')) {
232
+ capabilities.tensorCores = false;
233
+ capabilities.bf16 = false;
234
+ capabilities.fp8 = false;
235
+ capabilities.computeCapability = '6.0';
236
+ capabilities.architecture = 'Pascal';
237
+ }
212
238
  // RTX 50 series (Blackwell)
213
- if (nameLower.includes('rtx 50') || nameLower.includes('rtx50')) {
239
+ else if (nameLower.includes('rtx 50') || nameLower.includes('rtx50')) {
214
240
  capabilities.tensorCores = true;
215
241
  capabilities.bf16 = true;
216
242
  capabilities.fp8 = true;
@@ -257,15 +283,6 @@ class CUDADetector {
257
283
  capabilities.architecture = 'Volta';
258
284
  capabilities.nvlink = true;
259
285
  }
260
- // H100 (Hopper)
261
- else if (nameLower.includes('h100') || nameLower.includes('h200')) {
262
- capabilities.tensorCores = true;
263
- capabilities.bf16 = true;
264
- capabilities.fp8 = true;
265
- capabilities.nvlink = true;
266
- capabilities.computeCapability = '9.0';
267
- capabilities.architecture = 'Hopper';
268
- }
269
286
 
270
287
  return capabilities;
271
288
  }
@@ -311,6 +328,9 @@ class CUDADetector {
311
328
  'rtx 2060': 80,
312
329
 
313
330
  // Data center
331
+ 'gb10': 95,
332
+ 'grace blackwell': 95,
333
+ 'dgx spark': 95,
314
334
  'h100': 400,
315
335
  'h200': 450,
316
336
  'a100': 300,
@@ -318,7 +338,8 @@ class CUDADetector {
318
338
  'l4': 150,
319
339
  'a40': 180,
320
340
  't4': 70,
321
- 'v100': 120
341
+ 'v100': 120,
342
+ 'p100': 45
322
343
  };
323
344
 
324
345
  for (const [model, speed] of Object.entries(speedMap)) {
@@ -1,10 +1,12 @@
1
1
  const si = require('systeminformation');
2
+ const UnifiedDetector = require('./unified-detector');
2
3
 
3
4
  class HardwareDetector {
4
5
  constructor() {
5
6
  this.cache = null;
6
7
  this.cacheExpiry = 5 * 60 * 1000;
7
8
  this.cacheTime = 0;
9
+ this.unifiedDetector = new UnifiedDetector();
8
10
  }
9
11
 
10
12
  async getSystemInfo(forceFresh = false) {
@@ -31,6 +33,8 @@ class HardwareDetector {
31
33
  timestamp: Date.now()
32
34
  };
33
35
 
36
+ await this.enrichWithUnifiedHardware(systemInfo);
37
+
34
38
  this.cache = systemInfo;
35
39
  this.cacheTime = Date.now();
36
40
 
@@ -93,9 +97,15 @@ class HardwareDetector {
93
97
  const validGPUs = controllers.filter(gpu => {
94
98
  const model = (gpu.model || '').toLowerCase();
95
99
  const vendor = (gpu.vendor || '').toLowerCase();
100
+ const hasKnownModelSignature = this.looksLikeRealGPUModel(model);
96
101
 
97
102
  // Skip GPUs with empty/invalid data (like virtualized GPUs)
98
- if (!model || !vendor || model === 'unknown' || vendor === '') {
103
+ if (!model || model === 'unknown') {
104
+ return false;
105
+ }
106
+
107
+ // Some passthrough/virtualized setups report empty vendor while model is valid
108
+ if ((!vendor || vendor === '') && !hasKnownModelSignature) {
99
109
  return false;
100
110
  }
101
111
 
@@ -181,7 +191,7 @@ class HardwareDetector {
181
191
 
182
192
  return {
183
193
  model: enhancedModel,
184
- vendor: primaryGPU.vendor || 'Unknown',
194
+ vendor: primaryGPU.vendor || this.inferVendorFromGPUModel(enhancedModel, 'Unknown'),
185
195
  vram: effectiveVRAM,
186
196
  vramPerGPU: vram, // VRAM of primary GPU for reference
187
197
  vramDynamic: primaryGPU.vramDynamic || false,
@@ -192,13 +202,54 @@ class HardwareDetector {
192
202
  all: controllers.map(gpu => ({
193
203
  model: gpu.model,
194
204
  vram: this.normalizeVRAM(gpu.vram || 0),
195
- vendor: gpu.vendor
205
+ vendor: gpu.vendor || this.inferVendorFromGPUModel(gpu.model, 'Unknown')
196
206
  })),
197
207
  displays: displays.length,
198
208
  score: this.calculateGPUScore(primaryGPU)
199
209
  };
200
210
  }
201
211
 
212
+ async enrichWithUnifiedHardware(systemInfo) {
213
+ try {
214
+ const unified = await this.unifiedDetector.detect();
215
+ if (!unified || !unified.summary || !unified.primary) {
216
+ return;
217
+ }
218
+
219
+ const primaryType = unified.primary.type || 'cpu';
220
+ if (primaryType === 'cpu') {
221
+ return;
222
+ }
223
+
224
+ const summary = unified.summary;
225
+ const backendInfo = unified.backends?.[primaryType]?.info || {};
226
+ const backendGPUs = Array.isArray(backendInfo.gpus) ? backendInfo.gpus : [];
227
+ const gpuCount = summary.gpuCount || backendGPUs.length || systemInfo.gpu.gpuCount || 1;
228
+
229
+ const totalVRAM = typeof summary.totalVRAM === 'number' ? summary.totalVRAM : systemInfo.gpu.vram;
230
+ const perGPUVRAM = backendGPUs[0]?.memory?.total
231
+ || (gpuCount > 0 && totalVRAM > 0 ? Math.round(totalVRAM / gpuCount) : 0);
232
+
233
+ const modelFromUnified = summary.gpuModel || systemInfo.gpu.model;
234
+ const vendor = this.inferVendorFromGPUModel(modelFromUnified, systemInfo.gpu.vendor);
235
+
236
+ systemInfo.gpu = {
237
+ ...systemInfo.gpu,
238
+ model: modelFromUnified,
239
+ vendor,
240
+ vram: totalVRAM || systemInfo.gpu.vram,
241
+ vramPerGPU: perGPUVRAM || systemInfo.gpu.vramPerGPU || 0,
242
+ dedicated: primaryType !== 'metal',
243
+ gpuCount,
244
+ isMultiGPU: Boolean(summary.isMultiGPU || gpuCount > 1),
245
+ backend: primaryType,
246
+ driverVersion: backendInfo.driver || systemInfo.gpu.driverVersion
247
+ };
248
+ } catch (error) {
249
+ // Keep systeminformation-only results when backend-specific detection is unavailable
250
+ }
251
+ }
252
+
202
253
  processSystemInfo(system) {
203
254
  return {
204
255
  manufacturer: system.manufacturer || 'Unknown',
@@ -298,6 +349,10 @@ class HardwareDetector {
298
349
  estimateVRAMFromModel(model) {
299
350
  if (!model) return 0;
300
351
  const modelLower = model.toLowerCase();
352
+
353
+ // NVIDIA data-center / workstation
354
+ if (modelLower.includes('gb10') || modelLower.includes('grace blackwell') || modelLower.includes('dgx spark')) return 96;
355
+ if (modelLower.includes('tesla p100') || modelLower.includes('p100')) return 16;
301
356
 
302
357
  // NVIDIA RTX 50 series
303
358
  if (modelLower.includes('rtx 5090')) return 32;
@@ -398,6 +453,7 @@ class HardwareDetector {
398
453
 
399
454
  // Bonus por marcas/modelos específicos
400
455
  if (model.includes('rtx 5090')) score += 30;
456
+ else if (model.includes('gb10') || model.includes('grace blackwell') || model.includes('dgx spark')) score += 28;
401
457
  else if (model.includes('rtx 5080')) score += 27;
402
458
  else if (model.includes('rtx 5070')) score += 24;
403
459
  else if (model.includes('rtx 5060')) score += 21;
@@ -407,6 +463,7 @@ class HardwareDetector {
407
463
  else if (model.includes('rtx 30')) score += 18;
408
464
  else if (model.includes('rtx 20')) score += 15;
409
465
  else if (model.includes('gtx 16')) score += 12;
466
+ else if (model.includes('tesla p100') || model.includes('p100')) score += 14;
410
467
  else if (model.includes('apple m')) score += 15;
411
468
 
412
469
  return Math.min(Math.round(score), 100);
@@ -497,9 +554,10 @@ class HardwareDetector {
497
554
  */
498
555
  getGPUTier(model) {
499
556
  const modelLower = model.toLowerCase();
500
-
557
+
501
558
  // NVIDIA RTX series
502
559
  if (modelLower.includes('rtx 50')) return 100;
560
+ if (modelLower.includes('gb10') || modelLower.includes('grace blackwell') || modelLower.includes('dgx spark')) return 98;
503
561
  if (modelLower.includes('rtx 4090')) return 95;
504
562
  if (modelLower.includes('rtx 40')) return 90;
505
563
  if (modelLower.includes('rtx 3090')) return 85;
@@ -511,6 +569,7 @@ class HardwareDetector {
511
569
  // NVIDIA Professional
512
570
  if (modelLower.includes('a100')) return 98;
513
571
  if (modelLower.includes('h100')) return 99;
572
+ if (modelLower.includes('tesla p100') || modelLower.includes('p100')) return 78;
514
573
  if (modelLower.includes('tesla')) return 75;
515
574
  if (modelLower.includes('quadro')) return 65;
516
575
 
@@ -545,6 +604,49 @@ class HardwareDetector {
545
604
  return 0;
546
605
  }
547
606
 
607
+ looksLikeRealGPUModel(model) {
608
+ if (!model) return false;
609
+ const modelLower = model.toLowerCase();
610
+
611
+ const gpuMarkers = [
612
+ 'nvidia', 'geforce', 'rtx', 'gtx', 'tesla', 'quadro',
613
+ 'amd', 'radeon', 'rx ', 'instinct',
614
+ 'intel', 'arc', 'iris', 'uhd',
615
+ 'apple', 'm1', 'm2', 'm3', 'm4',
616
+ 'gb10', 'blackwell'
617
+ ];
618
+
619
+ return gpuMarkers.some(marker => modelLower.includes(marker));
620
+ }
621
+
622
+ inferVendorFromGPUModel(model, fallback = 'Unknown') {
623
+ if (!model) return fallback;
624
+ const modelLower = model.toLowerCase();
625
+
626
+ if (modelLower.includes('nvidia') || modelLower.includes('geforce') ||
627
+ modelLower.includes('rtx') || modelLower.includes('gtx') ||
628
+ modelLower.includes('tesla') || modelLower.includes('quadro') ||
629
+ modelLower.includes('gb10') || modelLower.includes('blackwell')) {
630
+ return 'NVIDIA';
631
+ }
632
+
633
+ if (modelLower.includes('amd') || modelLower.includes('radeon') || modelLower.includes('instinct')) {
634
+ return 'AMD';
635
+ }
636
+
637
+ if (modelLower.includes('intel') || modelLower.includes('arc') ||
638
+ modelLower.includes('iris') || modelLower.includes('uhd')) {
639
+ return 'Intel';
640
+ }
641
+
642
+ if (modelLower.includes('apple') || modelLower.includes('m1') ||
643
+ modelLower.includes('m2') || modelLower.includes('m3') || modelLower.includes('m4')) {
644
+ return 'Apple';
645
+ }
646
+
647
+ return fallback;
648
+ }
649
+
548
650
  async runQuickBenchmark() {
549
651
 
550
652
  const start = process.hrtime.bigint();
@@ -576,4 +678,4 @@ class HardwareDetector {
576
678
 
577
679
  }
578
680
 
579
- module.exports = HardwareDetector;
681
+ module.exports = HardwareDetector;
@@ -71,6 +71,13 @@ class HardwareSpecs {
71
71
  'NVIDIA GeForce RTX 3060 Ti': { score: 75, vram: 8, tdp: 200, dedicated: true },
72
72
  'NVIDIA GeForce RTX 3060': { score: 70, vram: 12, tdp: 170, dedicated: true },
73
73
 
74
+ // NVIDIA Data Center / Workstation
75
+ 'NVIDIA H100': { score: 100, vram: 80, tdp: 700, dedicated: true },
76
+ 'NVIDIA A100': { score: 94, vram: 80, tdp: 400, dedicated: true },
77
+ 'NVIDIA Tesla P100': { score: 74, vram: 16, tdp: 250, dedicated: true },
78
+ 'NVIDIA GB10 Grace Blackwell': { score: 96, vram: 96, tdp: 140, dedicated: true },
79
+ 'NVIDIA DGX Spark (GB10)': { score: 96, vram: 96, tdp: 140, dedicated: true },
80
+
74
81
  // AMD RX 7000 Series
75
82
  'AMD Radeon RX 7900 XTX': { score: 92, vram: 24, tdp: 355, dedicated: true },
76
83
  'AMD Radeon RX 7900 XT': { score: 88, vram: 20, tdp: 300, dedicated: true },
@@ -283,4 +290,4 @@ class HardwareSpecs {
283
290
  }
284
291
  }
285
292
 
286
- module.exports = HardwareSpecs;
293
+ module.exports = HardwareSpecs;