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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +418 -0
  3. package/analyzer/compatibility.js +584 -0
  4. package/analyzer/performance.js +505 -0
  5. package/bin/CLAUDE.md +12 -0
  6. package/bin/enhanced_cli.js +3118 -0
  7. package/bin/test-deterministic.js +41 -0
  8. package/package.json +96 -0
  9. package/src/CLAUDE.md +12 -0
  10. package/src/ai/intelligent-selector.js +615 -0
  11. package/src/ai/model-selector.js +312 -0
  12. package/src/ai/multi-objective-selector.js +820 -0
  13. package/src/commands/check.js +58 -0
  14. package/src/data/CLAUDE.md +11 -0
  15. package/src/data/model-database.js +637 -0
  16. package/src/data/sync-manager.js +279 -0
  17. package/src/hardware/CLAUDE.md +12 -0
  18. package/src/hardware/backends/CLAUDE.md +11 -0
  19. package/src/hardware/backends/apple-silicon.js +318 -0
  20. package/src/hardware/backends/cpu-detector.js +490 -0
  21. package/src/hardware/backends/cuda-detector.js +417 -0
  22. package/src/hardware/backends/intel-detector.js +436 -0
  23. package/src/hardware/backends/rocm-detector.js +440 -0
  24. package/src/hardware/detector.js +573 -0
  25. package/src/hardware/pc-optimizer.js +635 -0
  26. package/src/hardware/specs.js +286 -0
  27. package/src/hardware/unified-detector.js +442 -0
  28. package/src/index.js +2289 -0
  29. package/src/models/CLAUDE.md +17 -0
  30. package/src/models/ai-check-selector.js +806 -0
  31. package/src/models/catalog.json +426 -0
  32. package/src/models/deterministic-selector.js +1145 -0
  33. package/src/models/expanded_database.js +1142 -0
  34. package/src/models/intelligent-selector.js +532 -0
  35. package/src/models/requirements.js +310 -0
  36. package/src/models/scoring-config.js +57 -0
  37. package/src/models/scoring-engine.js +715 -0
  38. package/src/ollama/.cache/README.md +33 -0
  39. package/src/ollama/CLAUDE.md +24 -0
  40. package/src/ollama/client.js +438 -0
  41. package/src/ollama/enhanced-client.js +113 -0
  42. package/src/ollama/enhanced-scraper.js +634 -0
  43. package/src/ollama/manager.js +357 -0
  44. package/src/ollama/native-scraper.js +776 -0
  45. package/src/plugins/CLAUDE.md +11 -0
  46. package/src/plugins/examples/custom_model_plugin.js +87 -0
  47. package/src/plugins/index.js +295 -0
  48. package/src/utils/CLAUDE.md +11 -0
  49. package/src/utils/config.js +359 -0
  50. package/src/utils/formatter.js +315 -0
  51. package/src/utils/logger.js +272 -0
  52. package/src/utils/model-classifier.js +167 -0
  53. package/src/utils/verbose-progress.js +266 -0
@@ -0,0 +1,490 @@
1
+ /**
2
+ * CPU Detector
3
+ * Detects CPU capabilities for LLM inference
4
+ * Focuses on AVX2, AVX512, AMX, and other SIMD extensions
5
+ */
6
+
7
+ const { execSync } = require('child_process');
8
+ const os = require('os');
9
+ const fs = require('fs');
10
+
11
+ class CPUDetector {
12
+ constructor() {
13
+ this.cache = null;
14
+ }
15
+
16
+ /**
17
+ * Detect CPU capabilities
18
+ */
19
+ detect() {
20
+ if (this.cache) {
21
+ return this.cache;
22
+ }
23
+
24
+ try {
25
+ const info = this.getCPUInfo();
26
+ this.cache = info;
27
+ return info;
28
+ } catch (error) {
29
+ return this.getFallbackInfo();
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Get detailed CPU information
35
+ */
36
+ getCPUInfo() {
37
+ const cpus = os.cpus();
38
+ const cpu = cpus[0] || {};
39
+
40
+ const result = {
41
+ brand: cpu.model || 'Unknown CPU',
42
+ vendor: this.detectVendor(cpu.model),
43
+ cores: {
44
+ physical: this.getPhysicalCores(),
45
+ logical: cpus.length,
46
+ performance: 0,
47
+ efficiency: 0
48
+ },
49
+ frequency: {
50
+ base: cpu.speed || 0,
51
+ max: this.getMaxFrequency()
52
+ },
53
+ cache: this.getCacheInfo(),
54
+ capabilities: this.getCapabilities(),
55
+ architecture: process.arch,
56
+ backend: 'cpu',
57
+ speedCoefficient: 0
58
+ };
59
+
60
+ // Detect P-core/E-core for hybrid CPUs (Intel 12th+ gen, Apple Silicon)
61
+ const hybrid = this.detectHybridCores(result.brand);
62
+ result.cores.performance = hybrid.performance;
63
+ result.cores.efficiency = hybrid.efficiency;
64
+
65
+ // Calculate speed coefficient
66
+ result.speedCoefficient = this.calculateSpeedCoefficient(result);
67
+
68
+ return result;
69
+ }
70
+
71
+ /**
72
+ * Detect CPU vendor
73
+ */
74
+ detectVendor(brand) {
75
+ const brandLower = (brand || '').toLowerCase();
76
+
77
+ if (brandLower.includes('intel')) return 'Intel';
78
+ if (brandLower.includes('amd')) return 'AMD';
79
+ if (brandLower.includes('apple')) return 'Apple';
80
+ if (brandLower.includes('arm')) return 'ARM';
81
+ if (brandLower.includes('qualcomm')) return 'Qualcomm';
82
+
83
+ return 'Unknown';
84
+ }
85
+
86
+ /**
87
+ * Get physical core count
88
+ */
89
+ getPhysicalCores() {
90
+ try {
91
+ if (process.platform === 'darwin') {
92
+ return parseInt(execSync('sysctl -n hw.physicalcpu', { encoding: 'utf8', timeout: 5000 }).trim());
93
+ } else if (process.platform === 'linux') {
94
+ const cpuInfo = fs.readFileSync('/proc/cpuinfo', 'utf8');
95
+ const coreIds = new Set();
96
+ const matches = cpuInfo.matchAll(/core id\s*:\s*(\d+)/g);
97
+ for (const match of matches) {
98
+ coreIds.add(match[1]);
99
+ }
100
+ return coreIds.size || os.cpus().length;
101
+ } else if (process.platform === 'win32') {
102
+ const wmic = execSync('wmic cpu get NumberOfCores', { encoding: 'utf8', timeout: 5000 });
103
+ const match = wmic.match(/\d+/);
104
+ return match ? parseInt(match[0]) : os.cpus().length;
105
+ }
106
+ } catch (e) {
107
+ return os.cpus().length;
108
+ }
109
+ return os.cpus().length;
110
+ }
111
+
112
+ /**
113
+ * Get maximum CPU frequency
114
+ */
115
+ getMaxFrequency() {
116
+ try {
117
+ if (process.platform === 'darwin') {
118
+ // macOS doesn't expose max frequency easily
119
+ const cpus = os.cpus();
120
+ return cpus.length > 0 ? cpus[0].speed : 0;
121
+ } else if (process.platform === 'linux') {
122
+ const maxFreq = fs.readFileSync(
123
+ '/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq',
124
+ 'utf8'
125
+ );
126
+ return Math.round(parseInt(maxFreq) / 1000); // kHz to MHz
127
+ } else if (process.platform === 'win32') {
128
+ const wmic = execSync('wmic cpu get MaxClockSpeed', { encoding: 'utf8', timeout: 5000 });
129
+ const match = wmic.match(/\d+/);
130
+ return match ? parseInt(match[0]) : 0;
131
+ }
132
+ } catch (e) {
133
+ return os.cpus()[0]?.speed || 0;
134
+ }
135
+ return 0;
136
+ }
137
+
138
+ /**
139
+ * Get CPU cache information
140
+ */
141
+ getCacheInfo() {
142
+ const cache = {
143
+ l1d: 0,
144
+ l1i: 0,
145
+ l2: 0,
146
+ l3: 0
147
+ };
148
+
149
+ try {
150
+ if (process.platform === 'darwin') {
151
+ cache.l1d = parseInt(execSync('sysctl -n hw.l1dcachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 || 0;
152
+ cache.l1i = parseInt(execSync('sysctl -n hw.l1icachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 || 0;
153
+ cache.l2 = parseInt(execSync('sysctl -n hw.l2cachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 / 1024 || 0;
154
+ cache.l3 = parseInt(execSync('sysctl -n hw.l3cachesize', { encoding: 'utf8', timeout: 5000 })) / 1024 / 1024 || 0;
155
+ } else if (process.platform === 'linux') {
156
+ // Parse from /sys/devices/system/cpu/cpu0/cache/
157
+ const cachePath = '/sys/devices/system/cpu/cpu0/cache';
158
+ if (fs.existsSync(cachePath)) {
159
+ const indexes = fs.readdirSync(cachePath).filter(f => f.startsWith('index'));
160
+ for (const idx of indexes) {
161
+ try {
162
+ const level = fs.readFileSync(`${cachePath}/${idx}/level`, 'utf8').trim();
163
+ const type = fs.readFileSync(`${cachePath}/${idx}/type`, 'utf8').trim();
164
+ const size = fs.readFileSync(`${cachePath}/${idx}/size`, 'utf8').trim();
165
+
166
+ const sizeKB = parseInt(size.replace(/[KMG]/g, ''));
167
+ const multiplier = size.includes('M') ? 1024 : size.includes('G') ? 1024 * 1024 : 1;
168
+ const finalSize = sizeKB * multiplier;
169
+
170
+ if (level === '1' && type === 'Data') cache.l1d = finalSize;
171
+ else if (level === '1' && type === 'Instruction') cache.l1i = finalSize;
172
+ else if (level === '2') cache.l2 = finalSize / 1024; // MB
173
+ else if (level === '3') cache.l3 = finalSize / 1024; // MB
174
+ } catch (e) {
175
+ continue;
176
+ }
177
+ }
178
+ }
179
+ }
180
+ } catch (e) {
181
+ // Return zeros
182
+ }
183
+
184
+ return cache;
185
+ }
186
+
187
+ /**
188
+ * Get CPU SIMD and acceleration capabilities
189
+ */
190
+ getCapabilities() {
191
+ const caps = {
192
+ sse: false,
193
+ sse2: false,
194
+ sse3: false,
195
+ ssse3: false,
196
+ sse4_1: false,
197
+ sse4_2: false,
198
+ avx: false,
199
+ avx2: false,
200
+ avx512: false,
201
+ avx512_vnni: false,
202
+ amx: false, // Intel Advanced Matrix Extensions
203
+ fma: false,
204
+ f16c: false,
205
+ neon: false, // ARM NEON
206
+ sve: false, // ARM SVE
207
+ dotprod: false, // ARM dot product
208
+ bestSimd: 'none'
209
+ };
210
+
211
+ try {
212
+ if (process.platform === 'darwin') {
213
+ // For Apple Silicon (ARM64), use ARM features
214
+ if (process.arch === 'arm64') {
215
+ caps.neon = true; // All Apple Silicon has NEON
216
+ caps.dotprod = true;
217
+ caps.f16c = true;
218
+ // Apple Silicon has excellent FP16 support
219
+ } else {
220
+ // Intel Mac - check via sysctl
221
+ try {
222
+ const features = execSync('sysctl -n machdep.cpu.features', {
223
+ encoding: 'utf8',
224
+ timeout: 5000
225
+ }).toLowerCase();
226
+
227
+ const leafFeatures = execSync('sysctl -n machdep.cpu.leaf7_features', {
228
+ encoding: 'utf8',
229
+ timeout: 5000
230
+ }).toLowerCase();
231
+
232
+ caps.sse = features.includes('sse');
233
+ caps.sse2 = features.includes('sse2');
234
+ caps.sse3 = features.includes('sse3');
235
+ caps.ssse3 = features.includes('ssse3');
236
+ caps.sse4_1 = features.includes('sse4.1');
237
+ caps.sse4_2 = features.includes('sse4.2');
238
+ caps.avx = features.includes('avx1.0') || features.includes('avx ');
239
+ caps.avx2 = leafFeatures.includes('avx2');
240
+ caps.fma = features.includes('fma');
241
+ caps.f16c = leafFeatures.includes('f16c');
242
+
243
+ // Check for AVX-512 on Intel Macs
244
+ caps.avx512 = leafFeatures.includes('avx512');
245
+ } catch (e) {
246
+ // Intel Mac sysctl failed, assume basic SSE
247
+ caps.sse = caps.sse2 = true;
248
+ }
249
+ }
250
+
251
+ } else if (process.platform === 'linux') {
252
+ // Linux - check /proc/cpuinfo
253
+ const cpuInfo = fs.readFileSync('/proc/cpuinfo', 'utf8').toLowerCase();
254
+ const flags = cpuInfo.match(/flags\s*:\s*(.+)/)?.[1] || '';
255
+
256
+ caps.sse = flags.includes('sse ') || flags.includes(' sse');
257
+ caps.sse2 = flags.includes('sse2');
258
+ caps.sse3 = flags.includes('sse3');
259
+ caps.ssse3 = flags.includes('ssse3');
260
+ caps.sse4_1 = flags.includes('sse4_1');
261
+ caps.sse4_2 = flags.includes('sse4_2');
262
+ caps.avx = flags.includes('avx ') || flags.includes(' avx');
263
+ caps.avx2 = flags.includes('avx2');
264
+ caps.avx512 = flags.includes('avx512f');
265
+ caps.avx512_vnni = flags.includes('avx512_vnni');
266
+ caps.amx = flags.includes('amx_tile') || flags.includes('amx_int8');
267
+ caps.fma = flags.includes('fma');
268
+ caps.f16c = flags.includes('f16c');
269
+
270
+ // ARM features
271
+ if (process.arch === 'arm64') {
272
+ caps.neon = flags.includes('asimd') || flags.includes('neon');
273
+ caps.sve = flags.includes('sve');
274
+ caps.dotprod = flags.includes('asimddp');
275
+ }
276
+
277
+ } else if (process.platform === 'win32') {
278
+ // Windows - use WMIC or assume based on CPU model
279
+ const cpuName = os.cpus()[0]?.model?.toLowerCase() || '';
280
+
281
+ // All modern x64 CPUs have SSE through SSE4.2
282
+ if (process.arch === 'x64') {
283
+ caps.sse = caps.sse2 = caps.sse3 = caps.ssse3 = true;
284
+ caps.sse4_1 = caps.sse4_2 = true;
285
+
286
+ // Most post-2013 CPUs have AVX/AVX2
287
+ if (cpuName.includes('ryzen') || cpuName.includes('core')) {
288
+ caps.avx = true;
289
+ // Ryzen and Intel 4th gen+ have AVX2
290
+ if (cpuName.includes('ryzen') ||
291
+ cpuName.match(/core.*i[3579].*[4-9]\d{3}|1[0-4]\d{3}/)) {
292
+ caps.avx2 = true;
293
+ caps.fma = true;
294
+ caps.f16c = true;
295
+ }
296
+ }
297
+
298
+ // Intel 12th gen+ and Xeon Scalable have AVX-512
299
+ if (cpuName.includes('xeon') ||
300
+ cpuName.match(/12\d{3}|13\d{3}|14\d{3}/)) {
301
+ caps.avx512 = true;
302
+ }
303
+ }
304
+ }
305
+ } catch (e) {
306
+ // Assume modern x64 CPU has basic SIMD
307
+ if (process.arch === 'x64') {
308
+ caps.sse = caps.sse2 = true;
309
+ }
310
+ }
311
+
312
+ // Determine best SIMD level
313
+ if (caps.amx) caps.bestSimd = 'AMX';
314
+ else if (caps.avx512) caps.bestSimd = 'AVX512';
315
+ else if (caps.avx2) caps.bestSimd = 'AVX2';
316
+ else if (caps.avx) caps.bestSimd = 'AVX';
317
+ else if (caps.neon) caps.bestSimd = 'NEON';
318
+ else if (caps.sse4_2) caps.bestSimd = 'SSE4.2';
319
+ else if (caps.sse2) caps.bestSimd = 'SSE2';
320
+
321
+ return caps;
322
+ }
323
+
324
+ /**
325
+ * Detect hybrid core configuration
326
+ */
327
+ detectHybridCores(brand) {
328
+ const brandLower = (brand || '').toLowerCase();
329
+ const logicalCores = os.cpus().length;
330
+
331
+ // Intel 12th+ gen hybrid
332
+ if (brandLower.includes('intel') && brandLower.match(/12\d{3}|13\d{3}|14\d{3}/)) {
333
+ // Estimate P/E core split (varies by SKU)
334
+ if (brandLower.includes('i9')) {
335
+ return { performance: 8, efficiency: logicalCores - 16 };
336
+ } else if (brandLower.includes('i7')) {
337
+ return { performance: 8, efficiency: Math.max(0, logicalCores - 16) };
338
+ } else if (brandLower.includes('i5')) {
339
+ return { performance: 6, efficiency: Math.max(0, logicalCores - 12) };
340
+ }
341
+ }
342
+
343
+ // Apple Silicon (handled by apple-silicon.js, but fallback here)
344
+ if (brandLower.includes('apple') || brandLower.includes('m1') ||
345
+ brandLower.includes('m2') || brandLower.includes('m3') ||
346
+ brandLower.includes('m4')) {
347
+ const pCores = Math.ceil(logicalCores * 0.5);
348
+ return { performance: pCores, efficiency: logicalCores - pCores };
349
+ }
350
+
351
+ // Non-hybrid
352
+ return { performance: logicalCores, efficiency: 0 };
353
+ }
354
+
355
+ /**
356
+ * Calculate speed coefficient for LLM inference
357
+ */
358
+ calculateSpeedCoefficient(info) {
359
+ let base = 30; // Base tokens/sec per B params
360
+
361
+ // Adjust based on SIMD capabilities
362
+ switch (info.capabilities.bestSimd) {
363
+ case 'AMX':
364
+ base = 100; // Intel AMX is excellent for matrix ops
365
+ break;
366
+ case 'AVX512':
367
+ base = 70;
368
+ break;
369
+ case 'AVX2':
370
+ base = 50;
371
+ break;
372
+ case 'NEON':
373
+ base = 45; // ARM NEON is quite good
374
+ break;
375
+ case 'AVX':
376
+ base = 35;
377
+ break;
378
+ default:
379
+ base = 25;
380
+ }
381
+
382
+ // Adjust for core count (more cores = more parallel processing)
383
+ const coreBonus = Math.min(20, info.cores.physical * 1.5);
384
+ base += coreBonus;
385
+
386
+ // Adjust for frequency
387
+ const freqBonus = Math.min(15, (info.frequency.max / 1000) * 3);
388
+ base += freqBonus;
389
+
390
+ // Adjust for L3 cache (important for LLM)
391
+ if (info.cache.l3 >= 64) base += 15;
392
+ else if (info.cache.l3 >= 32) base += 10;
393
+ else if (info.cache.l3 >= 16) base += 5;
394
+
395
+ return Math.round(base);
396
+ }
397
+
398
+ /**
399
+ * Fallback info when detection fails
400
+ */
401
+ getFallbackInfo() {
402
+ const cpus = os.cpus();
403
+ const cpu = cpus[0] || {};
404
+
405
+ return {
406
+ brand: cpu.model || 'Unknown CPU',
407
+ vendor: 'Unknown',
408
+ cores: {
409
+ physical: cpus.length,
410
+ logical: cpus.length,
411
+ performance: cpus.length,
412
+ efficiency: 0
413
+ },
414
+ frequency: {
415
+ base: cpu.speed || 0,
416
+ max: cpu.speed || 0
417
+ },
418
+ cache: { l1d: 0, l1i: 0, l2: 0, l3: 0 },
419
+ capabilities: {
420
+ sse2: process.arch === 'x64',
421
+ avx: false,
422
+ avx2: false,
423
+ avx512: false,
424
+ neon: process.arch === 'arm64',
425
+ bestSimd: process.arch === 'arm64' ? 'NEON' : 'SSE2'
426
+ },
427
+ architecture: process.arch,
428
+ backend: 'cpu',
429
+ speedCoefficient: 30
430
+ };
431
+ }
432
+
433
+ /**
434
+ * Get hardware fingerprint
435
+ */
436
+ getFingerprint() {
437
+ const info = this.detect();
438
+ const brandSlug = info.brand.toLowerCase()
439
+ .replace(/\(r\)|\(tm\)|@|cpu|processor/gi, '')
440
+ .replace(/\s+/g, '-')
441
+ .replace(/-+/g, '-')
442
+ .trim();
443
+
444
+ return `cpu-${brandSlug}-${info.cores.logical}c-${info.capabilities.bestSimd.toLowerCase()}`;
445
+ }
446
+
447
+ /**
448
+ * Estimate inference speed
449
+ */
450
+ estimateTokensPerSecond(paramsB, quantization = 'Q4_K_M') {
451
+ const info = this.detect();
452
+
453
+ // CPU quantization benefits
454
+ const quantMult = {
455
+ 'FP16': 0.8, // FP16 slower on CPU
456
+ 'Q8_0': 1.2,
457
+ 'Q6_K': 1.5,
458
+ 'Q5_K_M': 1.8,
459
+ 'Q5_0': 1.8,
460
+ 'Q4_K_M': 2.2,
461
+ 'Q4_0': 2.5,
462
+ 'Q3_K_M': 2.8,
463
+ 'Q2_K': 3.2,
464
+ 'IQ4_XS': 2.3,
465
+ 'IQ3_XXS': 2.9
466
+ };
467
+
468
+ const mult = quantMult[quantization] || 2.0;
469
+ const baseSpeed = info.speedCoefficient / paramsB * mult;
470
+
471
+ return Math.round(baseSpeed);
472
+ }
473
+
474
+ /**
475
+ * Get recommended thread count for inference
476
+ */
477
+ getRecommendedThreads() {
478
+ const info = this.detect();
479
+
480
+ // Use performance cores if hybrid, otherwise physical cores
481
+ // Leave 1-2 cores for system
482
+ const available = info.cores.performance > 0
483
+ ? info.cores.performance
484
+ : info.cores.physical;
485
+
486
+ return Math.max(1, available - 1);
487
+ }
488
+ }
489
+
490
+ module.exports = CPUDetector;