llm-checker 3.5.2 → 3.5.3
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/bin/enhanced_cli.js +309 -1
- package/package.json +10 -3
- package/src/hardware/detector.js +13 -0
- package/src/hardware/profiles.js +484 -0
- package/src/index.js +19 -1
- package/src/ui/cli-theme.js +32 -85
- package/src/ui/interactive-panel.js +1 -0
- package/src/data/CLAUDE.md +0 -17
- package/src/hardware/CLAUDE.md +0 -18
- package/src/hardware/backends/CLAUDE.md +0 -17
- package/src/models/CLAUDE.md +0 -23
- package/src/ollama/CLAUDE.md +0 -30
- package/src/plugins/CLAUDE.md +0 -17
- package/src/utils/CLAUDE.md +0 -17
package/bin/enhanced_cli.js
CHANGED
|
@@ -65,6 +65,7 @@ const COMMAND_HEADER_LABELS = {
|
|
|
65
65
|
demo: 'Demo',
|
|
66
66
|
ollama: 'Ollama Integration',
|
|
67
67
|
recommend: 'Recommendations',
|
|
68
|
+
simulate: 'Hardware Simulation',
|
|
68
69
|
'list-models': 'Model Catalog'
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -2938,6 +2939,11 @@ program
|
|
|
2938
2939
|
.option('--performance-test', 'Run performance benchmarks')
|
|
2939
2940
|
.option('--show-ollama-analysis', 'Show detailed Ollama model analysis')
|
|
2940
2941
|
.option('--no-verbose', 'Disable step-by-step progress display')
|
|
2942
|
+
.option('--simulate <profile>', 'Simulate a hardware profile instead of detecting real hardware (use "list" to see profiles)')
|
|
2943
|
+
.option('--gpu <model>', 'Custom GPU model for simulation (e.g., "RTX 5060", "RX 7800 XT")')
|
|
2944
|
+
.option('--ram <gb>', 'Custom RAM in GB for simulation (e.g., 32)')
|
|
2945
|
+
.option('--cpu <model>', 'Custom CPU model for simulation (e.g., "AMD Ryzen 7 5700X")')
|
|
2946
|
+
.option('--vram <gb>', 'Override GPU VRAM in GB for simulation (auto-detected if omitted)')
|
|
2941
2947
|
.addHelpText(
|
|
2942
2948
|
'after',
|
|
2943
2949
|
`
|
|
@@ -2946,6 +2952,12 @@ Enterprise policy examples:
|
|
|
2946
2952
|
$ llm-checker check --policy ./policy.yaml --use-case coding --runtime vllm
|
|
2947
2953
|
$ llm-checker check --policy ./policy.yaml --include-cloud --max-size 24B
|
|
2948
2954
|
|
|
2955
|
+
Hardware simulation:
|
|
2956
|
+
$ llm-checker check --simulate list
|
|
2957
|
+
$ llm-checker check --simulate rtx4090
|
|
2958
|
+
$ llm-checker check --simulate m4pro24 --use-case coding
|
|
2959
|
+
$ llm-checker check --gpu "RTX 5060" --ram 32 --cpu "AMD Ryzen 7 5700X"
|
|
2960
|
+
|
|
2949
2961
|
Policy scope:
|
|
2950
2962
|
- Evaluates all compatible and marginal candidates discovered during analysis
|
|
2951
2963
|
- Not limited to the top --limit results shown in output
|
|
@@ -2958,7 +2970,57 @@ Policy scope:
|
|
|
2958
2970
|
const verboseEnabled = options.verbose !== false;
|
|
2959
2971
|
const checker = new (getLLMChecker())({ verbose: verboseEnabled });
|
|
2960
2972
|
const policyConfig = options.policy ? loadPolicyConfiguration(options.policy) : null;
|
|
2961
|
-
|
|
2973
|
+
|
|
2974
|
+
// Handle hardware simulation (preset profile or custom flags)
|
|
2975
|
+
const hasCustomHwFlags = options.gpu || options.ram || options.cpu || options.vram;
|
|
2976
|
+
if (options.simulate || hasCustomHwFlags) {
|
|
2977
|
+
const { buildFullHardwareObject, buildCustomHardwareObject, getProfile, listProfiles } = require('../src/hardware/profiles');
|
|
2978
|
+
if (options.simulate === 'list') {
|
|
2979
|
+
console.log(chalk.cyan.bold('\n Available Hardware Profiles:\n'));
|
|
2980
|
+
listProfiles().forEach(line => console.log(line));
|
|
2981
|
+
console.log('');
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
let simulatedHardware;
|
|
2985
|
+
let displayLabel;
|
|
2986
|
+
if (hasCustomHwFlags) {
|
|
2987
|
+
const ramValue = options.ram ? parseInt(options.ram) : undefined;
|
|
2988
|
+
const vramValue = options.vram ? parseInt(options.vram) : undefined;
|
|
2989
|
+
if (options.vram && !options.gpu) {
|
|
2990
|
+
console.error(chalk.red('\n --vram requires --gpu in custom hardware mode (e.g., --gpu "RTX 4090" --vram 24).'));
|
|
2991
|
+
process.exit(1);
|
|
2992
|
+
}
|
|
2993
|
+
if (options.ram && (!Number.isFinite(ramValue) || ramValue <= 0)) {
|
|
2994
|
+
console.error(chalk.red(`\n Invalid --ram value: "${options.ram}". Must be a positive number (e.g., 32).`));
|
|
2995
|
+
process.exit(1);
|
|
2996
|
+
}
|
|
2997
|
+
if (options.vram && (!Number.isFinite(vramValue) || vramValue <= 0)) {
|
|
2998
|
+
console.error(chalk.red(`\n Invalid --vram value: "${options.vram}". Must be a positive number (e.g., 8).`));
|
|
2999
|
+
process.exit(1);
|
|
3000
|
+
}
|
|
3001
|
+
simulatedHardware = buildCustomHardwareObject({
|
|
3002
|
+
gpu: options.gpu || null,
|
|
3003
|
+
ram: ramValue,
|
|
3004
|
+
cpu: options.cpu || null,
|
|
3005
|
+
vram: vramValue
|
|
3006
|
+
});
|
|
3007
|
+
displayLabel = simulatedHardware._displayName;
|
|
3008
|
+
} else {
|
|
3009
|
+
const profile = getProfile(options.simulate);
|
|
3010
|
+
if (!profile) {
|
|
3011
|
+
console.error(chalk.red(`\n Unknown profile: ${options.simulate}`));
|
|
3012
|
+
console.log(chalk.gray('\n Available profiles:'));
|
|
3013
|
+
listProfiles().forEach(line => console.log(line));
|
|
3014
|
+
console.log('');
|
|
3015
|
+
process.exit(1);
|
|
3016
|
+
}
|
|
3017
|
+
simulatedHardware = buildFullHardwareObject(options.simulate);
|
|
3018
|
+
displayLabel = profile.displayName;
|
|
3019
|
+
}
|
|
3020
|
+
checker.setSimulatedHardware(simulatedHardware);
|
|
3021
|
+
console.log(chalk.magenta.bold(`\n SIMULATION MODE: ${displayLabel}\n`));
|
|
3022
|
+
}
|
|
3023
|
+
|
|
2962
3024
|
// If verbose is disabled, show simple loading message
|
|
2963
3025
|
if (!verboseEnabled) {
|
|
2964
3026
|
process.stdout.write(chalk.gray('Analyzing your system...'));
|
|
@@ -3429,6 +3491,11 @@ program
|
|
|
3429
3491
|
.option('--optimize <profile>', 'Optimization profile (balanced|speed|quality|context|coding)', 'balanced')
|
|
3430
3492
|
.option('--no-verbose', 'Disable step-by-step progress display')
|
|
3431
3493
|
.option('--policy <file>', 'Evaluate recommendations against a policy file')
|
|
3494
|
+
.option('--simulate <profile>', 'Simulate a hardware profile instead of detecting real hardware (use "list" to see profiles)')
|
|
3495
|
+
.option('--gpu <model>', 'Custom GPU model for simulation (e.g., "RTX 5060", "RX 7800 XT")')
|
|
3496
|
+
.option('--ram <gb>', 'Custom RAM in GB for simulation (e.g., 32)')
|
|
3497
|
+
.option('--cpu <model>', 'Custom CPU model for simulation (e.g., "AMD Ryzen 7 5700X")')
|
|
3498
|
+
.option('--vram <gb>', 'Override GPU VRAM in GB for simulation (auto-detected if omitted)')
|
|
3432
3499
|
.option(
|
|
3433
3500
|
'--calibrated [file]',
|
|
3434
3501
|
'Use calibrated routing policy (optional file path; defaults to ~/.llm-checker/calibration-policy.{yaml,yml,json})'
|
|
@@ -3441,6 +3508,11 @@ Enterprise policy examples:
|
|
|
3441
3508
|
$ llm-checker recommend --policy ./policy.yaml --category coding
|
|
3442
3509
|
$ llm-checker recommend --policy ./policy.yaml --no-verbose
|
|
3443
3510
|
|
|
3511
|
+
Hardware simulation:
|
|
3512
|
+
$ llm-checker recommend --simulate rtx4090
|
|
3513
|
+
$ llm-checker recommend --simulate m4pro24 --category coding
|
|
3514
|
+
$ llm-checker recommend --gpu "RTX 5060" --ram 32 --cpu "AMD Ryzen 7 5700X"
|
|
3515
|
+
|
|
3444
3516
|
Calibrated routing examples:
|
|
3445
3517
|
$ llm-checker recommend --calibrated --category coding
|
|
3446
3518
|
$ llm-checker recommend --calibrated ./calibration-policy.yaml --category reasoning
|
|
@@ -3452,6 +3524,57 @@ Calibrated routing examples:
|
|
|
3452
3524
|
try {
|
|
3453
3525
|
const verboseEnabled = options.verbose !== false;
|
|
3454
3526
|
const checker = new (getLLMChecker())({ verbose: verboseEnabled });
|
|
3527
|
+
|
|
3528
|
+
// Handle hardware simulation (preset profile or custom flags)
|
|
3529
|
+
const hasCustomHwFlags = options.gpu || options.ram || options.cpu || options.vram;
|
|
3530
|
+
if (options.simulate || hasCustomHwFlags) {
|
|
3531
|
+
const { buildFullHardwareObject, buildCustomHardwareObject, getProfile, listProfiles } = require('../src/hardware/profiles');
|
|
3532
|
+
if (options.simulate === 'list') {
|
|
3533
|
+
console.log(chalk.cyan.bold('\n Available Hardware Profiles:\n'));
|
|
3534
|
+
listProfiles().forEach(line => console.log(line));
|
|
3535
|
+
console.log('');
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
let simulatedHardware;
|
|
3539
|
+
let displayLabel;
|
|
3540
|
+
if (hasCustomHwFlags) {
|
|
3541
|
+
const ramValue = options.ram ? parseInt(options.ram) : undefined;
|
|
3542
|
+
const vramValue = options.vram ? parseInt(options.vram) : undefined;
|
|
3543
|
+
if (options.vram && !options.gpu) {
|
|
3544
|
+
console.error(chalk.red('\n --vram requires --gpu in custom hardware mode (e.g., --gpu "RTX 4090" --vram 24).'));
|
|
3545
|
+
process.exit(1);
|
|
3546
|
+
}
|
|
3547
|
+
if (options.ram && (!Number.isFinite(ramValue) || ramValue <= 0)) {
|
|
3548
|
+
console.error(chalk.red(`\n Invalid --ram value: "${options.ram}". Must be a positive number (e.g., 32).`));
|
|
3549
|
+
process.exit(1);
|
|
3550
|
+
}
|
|
3551
|
+
if (options.vram && (!Number.isFinite(vramValue) || vramValue <= 0)) {
|
|
3552
|
+
console.error(chalk.red(`\n Invalid --vram value: "${options.vram}". Must be a positive number (e.g., 8).`));
|
|
3553
|
+
process.exit(1);
|
|
3554
|
+
}
|
|
3555
|
+
simulatedHardware = buildCustomHardwareObject({
|
|
3556
|
+
gpu: options.gpu || null,
|
|
3557
|
+
ram: ramValue,
|
|
3558
|
+
cpu: options.cpu || null,
|
|
3559
|
+
vram: vramValue
|
|
3560
|
+
});
|
|
3561
|
+
displayLabel = simulatedHardware._displayName;
|
|
3562
|
+
} else {
|
|
3563
|
+
const profile = getProfile(options.simulate);
|
|
3564
|
+
if (!profile) {
|
|
3565
|
+
console.error(chalk.red(`\n Unknown profile: ${options.simulate}`));
|
|
3566
|
+
console.log(chalk.gray('\n Available profiles:'));
|
|
3567
|
+
listProfiles().forEach(line => console.log(line));
|
|
3568
|
+
console.log('');
|
|
3569
|
+
process.exit(1);
|
|
3570
|
+
}
|
|
3571
|
+
simulatedHardware = buildFullHardwareObject(options.simulate);
|
|
3572
|
+
displayLabel = profile.displayName;
|
|
3573
|
+
}
|
|
3574
|
+
checker.setSimulatedHardware(simulatedHardware);
|
|
3575
|
+
console.log(chalk.magenta.bold(`\n SIMULATION MODE: ${displayLabel}\n`));
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3455
3578
|
const routingPreference = resolveRoutingPolicyPreference({
|
|
3456
3579
|
policyOption: options.policy,
|
|
3457
3580
|
calibratedOption: options.calibrated,
|
|
@@ -3524,6 +3647,191 @@ Calibrated routing examples:
|
|
|
3524
3647
|
}
|
|
3525
3648
|
});
|
|
3526
3649
|
|
|
3650
|
+
program
|
|
3651
|
+
.command('simulate')
|
|
3652
|
+
.description('Simulate hardware profiles to see compatible LLM models for different systems')
|
|
3653
|
+
.option('-p, --profile <name>', 'Hardware profile to simulate (e.g., rtx4090, m4pro24, h100)')
|
|
3654
|
+
.option('-l, --list', 'List all available hardware profiles')
|
|
3655
|
+
.option('--gpu <model>', 'Custom GPU model (e.g., "RTX 5060", "RX 7800 XT", "Apple M4 Pro")')
|
|
3656
|
+
.option('--ram <gb>', 'Custom RAM in GB (e.g., 32)')
|
|
3657
|
+
.option('--cpu <model>', 'Custom CPU model (e.g., "AMD Ryzen 7 5700X")')
|
|
3658
|
+
.option('--vram <gb>', 'Override GPU VRAM in GB (auto-detected from GPU model if omitted)')
|
|
3659
|
+
.option('-u, --use-case <case>', 'Specify use case', 'general')
|
|
3660
|
+
.option('--optimize <profile>', 'Optimization profile (balanced|speed|quality|context|coding)', 'balanced')
|
|
3661
|
+
.option('--limit <number>', 'Number of compatible models to show (default: 1)', '1')
|
|
3662
|
+
.option('--no-verbose', 'Disable step-by-step progress display')
|
|
3663
|
+
.addHelpText(
|
|
3664
|
+
'after',
|
|
3665
|
+
`
|
|
3666
|
+
Preset profiles:
|
|
3667
|
+
$ llm-checker simulate --list
|
|
3668
|
+
$ llm-checker simulate
|
|
3669
|
+
$ llm-checker simulate -p rtx4090
|
|
3670
|
+
$ llm-checker simulate -p m4pro24 --use-case coding
|
|
3671
|
+
|
|
3672
|
+
Custom hardware:
|
|
3673
|
+
$ llm-checker simulate --gpu "RTX 5060" --ram 32 --cpu "AMD Ryzen 7 5700X"
|
|
3674
|
+
$ llm-checker simulate --gpu "RTX 4090" --ram 64
|
|
3675
|
+
$ llm-checker simulate --gpu "RX 7800 XT" --ram 32 --vram 16
|
|
3676
|
+
$ llm-checker simulate --ram 16
|
|
3677
|
+
`
|
|
3678
|
+
)
|
|
3679
|
+
.action(async (options) => {
|
|
3680
|
+
const { buildFullHardwareObject, buildCustomHardwareObject, getProfile, getProfilesByCategory, listProfiles, CATEGORY_LABELS } = require('../src/hardware/profiles');
|
|
3681
|
+
|
|
3682
|
+
// List mode
|
|
3683
|
+
if (options.list) {
|
|
3684
|
+
console.log(chalk.cyan.bold('\n Available Hardware Profiles:\n'));
|
|
3685
|
+
listProfiles().forEach(line => console.log(line));
|
|
3686
|
+
console.log('');
|
|
3687
|
+
return;
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
let simulatedHardware;
|
|
3691
|
+
let displayLabel;
|
|
3692
|
+
|
|
3693
|
+
// Custom hardware mode: --gpu, --ram, --cpu, --vram
|
|
3694
|
+
const hasCustomFlags = options.gpu || options.ram || options.cpu || options.vram;
|
|
3695
|
+
if (hasCustomFlags) {
|
|
3696
|
+
const ramValue = options.ram ? parseInt(options.ram) : undefined;
|
|
3697
|
+
const vramValue = options.vram ? parseInt(options.vram) : undefined;
|
|
3698
|
+
if (options.vram && !options.gpu) {
|
|
3699
|
+
console.error(chalk.red('\n --vram requires --gpu in custom hardware mode (e.g., --gpu "RTX 4090" --vram 24).'));
|
|
3700
|
+
process.exit(1);
|
|
3701
|
+
}
|
|
3702
|
+
if (options.ram && (!Number.isFinite(ramValue) || ramValue <= 0)) {
|
|
3703
|
+
console.error(chalk.red(`\n Invalid --ram value: "${options.ram}". Must be a positive number (e.g., 32).`));
|
|
3704
|
+
process.exit(1);
|
|
3705
|
+
}
|
|
3706
|
+
if (options.vram && (!Number.isFinite(vramValue) || vramValue <= 0)) {
|
|
3707
|
+
console.error(chalk.red(`\n Invalid --vram value: "${options.vram}". Must be a positive number (e.g., 8).`));
|
|
3708
|
+
process.exit(1);
|
|
3709
|
+
}
|
|
3710
|
+
simulatedHardware = buildCustomHardwareObject({
|
|
3711
|
+
gpu: options.gpu || null,
|
|
3712
|
+
ram: ramValue,
|
|
3713
|
+
cpu: options.cpu || null,
|
|
3714
|
+
vram: vramValue
|
|
3715
|
+
});
|
|
3716
|
+
displayLabel = simulatedHardware._displayName;
|
|
3717
|
+
} else {
|
|
3718
|
+
// Preset profile mode
|
|
3719
|
+
if (!options.profile) {
|
|
3720
|
+
// Guard against non-interactive environments
|
|
3721
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3722
|
+
console.error(chalk.red('\n No hardware profile specified.'));
|
|
3723
|
+
console.log(chalk.gray(' Use --profile <name>, --gpu/--ram/--cpu flags, or --list to see profiles.\n'));
|
|
3724
|
+
process.exit(1);
|
|
3725
|
+
}
|
|
3726
|
+
// Interactive selection
|
|
3727
|
+
try {
|
|
3728
|
+
const inquirer = require('inquirer');
|
|
3729
|
+
const categories = getProfilesByCategory();
|
|
3730
|
+
const choices = [];
|
|
3731
|
+
|
|
3732
|
+
for (const [category, profiles] of Object.entries(categories)) {
|
|
3733
|
+
const label = CATEGORY_LABELS[category] || category;
|
|
3734
|
+
choices.push(new inquirer.Separator(chalk.gray(`── ${label} ──`)));
|
|
3735
|
+
for (const [key, profile] of Object.entries(profiles)) {
|
|
3736
|
+
const vramLabel = profile.gpu.unified
|
|
3737
|
+
? `${profile.memory.total}GB unified`
|
|
3738
|
+
: (profile.gpu.vram > 0 ? `${profile.gpu.vram}GB VRAM` : 'No GPU');
|
|
3739
|
+
const ramLabel = profile.gpu.unified ? '' : ` / ${profile.memory.total}GB RAM`;
|
|
3740
|
+
choices.push({
|
|
3741
|
+
name: `${profile.displayName} ${chalk.gray(`(${vramLabel}${ramLabel})`)}`,
|
|
3742
|
+
value: key
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3747
|
+
const { selectedProfile } = await inquirer.prompt([{
|
|
3748
|
+
type: 'list',
|
|
3749
|
+
name: 'selectedProfile',
|
|
3750
|
+
message: 'Select a hardware profile to simulate:',
|
|
3751
|
+
choices,
|
|
3752
|
+
pageSize: 20
|
|
3753
|
+
}]);
|
|
3754
|
+
options.profile = selectedProfile;
|
|
3755
|
+
} catch (error) {
|
|
3756
|
+
if (error.isTtyError) {
|
|
3757
|
+
console.error(chalk.red('Interactive mode requires a TTY terminal.'));
|
|
3758
|
+
console.log(chalk.gray('Use --profile <name>, --gpu/--ram flags, or --list to see available profiles.'));
|
|
3759
|
+
process.exit(1);
|
|
3760
|
+
}
|
|
3761
|
+
throw error;
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3765
|
+
// Validate profile
|
|
3766
|
+
const profile = getProfile(options.profile);
|
|
3767
|
+
if (!profile) {
|
|
3768
|
+
console.error(chalk.red(`\n Unknown profile: ${options.profile}`));
|
|
3769
|
+
console.log(chalk.gray('\n Available profiles:'));
|
|
3770
|
+
listProfiles().forEach(line => console.log(line));
|
|
3771
|
+
console.log('');
|
|
3772
|
+
process.exit(1);
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3775
|
+
simulatedHardware = buildFullHardwareObject(options.profile);
|
|
3776
|
+
displayLabel = profile.displayName;
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
showAsciiArt('simulate');
|
|
3780
|
+
|
|
3781
|
+
try {
|
|
3782
|
+
const verboseEnabled = options.verbose !== false;
|
|
3783
|
+
const checker = new (getLLMChecker())({ verbose: verboseEnabled });
|
|
3784
|
+
checker.setSimulatedHardware(simulatedHardware);
|
|
3785
|
+
|
|
3786
|
+
console.log(chalk.magenta.bold(` SIMULATION MODE: ${displayLabel}\n`));
|
|
3787
|
+
|
|
3788
|
+
if (!verboseEnabled) {
|
|
3789
|
+
process.stdout.write(chalk.gray('Analyzing simulated hardware...'));
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
const hardware = await checker.getSystemInfo();
|
|
3793
|
+
|
|
3794
|
+
const normalizeUseCase = (useCase = '') => {
|
|
3795
|
+
const alias = useCase.toLowerCase().trim();
|
|
3796
|
+
const useCaseMap = {
|
|
3797
|
+
'embed': 'embeddings', 'embedding': 'embeddings', 'embeddings': 'embeddings',
|
|
3798
|
+
'embedings': 'embeddings', 'talk': 'chat', 'chat': 'chat', 'talking': 'chat'
|
|
3799
|
+
};
|
|
3800
|
+
return useCaseMap[alias] || alias || 'general';
|
|
3801
|
+
};
|
|
3802
|
+
|
|
3803
|
+
const analysis = await checker.analyze({
|
|
3804
|
+
useCase: normalizeUseCase(options.useCase),
|
|
3805
|
+
limit: parseInt(options.limit) || 10,
|
|
3806
|
+
runtime: 'ollama'
|
|
3807
|
+
});
|
|
3808
|
+
|
|
3809
|
+
if (!verboseEnabled) {
|
|
3810
|
+
console.log(chalk.green(' done'));
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
displaySimplifiedSystemInfo(hardware);
|
|
3814
|
+
|
|
3815
|
+
const normalizedUseCase = normalizeUseCase(options.useCase);
|
|
3816
|
+
const limit = parseInt(options.limit) || 1;
|
|
3817
|
+
const recommendedModels = await displayModelRecommendations(
|
|
3818
|
+
analysis,
|
|
3819
|
+
hardware,
|
|
3820
|
+
normalizedUseCase,
|
|
3821
|
+
limit,
|
|
3822
|
+
'ollama'
|
|
3823
|
+
);
|
|
3824
|
+
await displayQuickStartCommands(analysis, recommendedModels[0], recommendedModels, 'ollama');
|
|
3825
|
+
|
|
3826
|
+
} catch (error) {
|
|
3827
|
+
console.error(chalk.red('\nError:'), error.message);
|
|
3828
|
+
if (process.env.DEBUG) {
|
|
3829
|
+
console.error(error.stack);
|
|
3830
|
+
}
|
|
3831
|
+
process.exit(1);
|
|
3832
|
+
}
|
|
3833
|
+
});
|
|
3834
|
+
|
|
3527
3835
|
program
|
|
3528
3836
|
.command('list-models')
|
|
3529
3837
|
.description('List all models from Ollama database')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-checker",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.3",
|
|
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
6
|
"llm-checker": "bin/cli.js",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"inquirer": "^8.2.6",
|
|
48
48
|
"node-fetch": "^2.7.0",
|
|
49
49
|
"ora": "^5.4.1",
|
|
50
|
-
"systeminformation": "^5.
|
|
50
|
+
"systeminformation": "^5.31.1",
|
|
51
51
|
"table": "^6.8.1",
|
|
52
52
|
"yaml": "^2.8.1",
|
|
53
53
|
"zod": "^3.23.0"
|
|
@@ -55,9 +55,16 @@
|
|
|
55
55
|
"optionalDependencies": {
|
|
56
56
|
"sql.js": "^1.14.0"
|
|
57
57
|
},
|
|
58
|
+
"overrides": {
|
|
59
|
+
"ajv": "^8.18.0",
|
|
60
|
+
"hono": "^4.11.10",
|
|
61
|
+
"glob": "^13.0.0",
|
|
62
|
+
"minimatch": "^10.2.2",
|
|
63
|
+
"test-exclude": "^7.0.1"
|
|
64
|
+
},
|
|
58
65
|
"devDependencies": {
|
|
59
66
|
"@types/node": "^20.0.0",
|
|
60
|
-
"jest": "^
|
|
67
|
+
"jest": "^30.2.0"
|
|
61
68
|
},
|
|
62
69
|
"keywords": [
|
|
63
70
|
"llm",
|
package/src/hardware/detector.js
CHANGED
|
@@ -7,9 +7,22 @@ class HardwareDetector {
|
|
|
7
7
|
this.cacheExpiry = 5 * 60 * 1000;
|
|
8
8
|
this.cacheTime = 0;
|
|
9
9
|
this.unifiedDetector = new UnifiedDetector();
|
|
10
|
+
this._simulatedHardware = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setSimulatedHardware(hardwareObject) {
|
|
14
|
+
this._simulatedHardware = hardwareObject;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
clearSimulatedHardware() {
|
|
18
|
+
this._simulatedHardware = null;
|
|
10
19
|
}
|
|
11
20
|
|
|
12
21
|
async getSystemInfo(forceFresh = false) {
|
|
22
|
+
// Return simulated hardware if set (bypasses real detection)
|
|
23
|
+
if (this._simulatedHardware) {
|
|
24
|
+
return this._simulatedHardware;
|
|
25
|
+
}
|
|
13
26
|
|
|
14
27
|
if (!forceFresh && this.cache && (Date.now() - this.cacheTime < this.cacheExpiry)) {
|
|
15
28
|
return this.cache;
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
const HardwareSpecs = require('./specs');
|
|
2
|
+
|
|
3
|
+
const specs = new HardwareSpecs();
|
|
4
|
+
|
|
5
|
+
// Curated hardware profiles for simulation
|
|
6
|
+
const HARDWARE_PROFILES = {
|
|
7
|
+
// NVIDIA Data Center
|
|
8
|
+
h100: {
|
|
9
|
+
displayName: 'NVIDIA H100 80GB (Data Center)',
|
|
10
|
+
category: 'data_center',
|
|
11
|
+
gpu: { model: 'NVIDIA H100', vendor: 'NVIDIA', vram: 80, dedicated: true },
|
|
12
|
+
cpu: { brand: 'AMD EPYC 9654', cores: 96, physicalCores: 96, speed: 2.4, architecture: 'x86_64' },
|
|
13
|
+
memory: { total: 256 },
|
|
14
|
+
backend: 'cuda',
|
|
15
|
+
os: { platform: 'linux' }
|
|
16
|
+
},
|
|
17
|
+
a100: {
|
|
18
|
+
displayName: 'NVIDIA A100 80GB (Data Center)',
|
|
19
|
+
category: 'data_center',
|
|
20
|
+
gpu: { model: 'NVIDIA A100', vendor: 'NVIDIA', vram: 80, dedicated: true },
|
|
21
|
+
cpu: { brand: 'AMD EPYC 7763', cores: 64, physicalCores: 64, speed: 2.45, architecture: 'x86_64' },
|
|
22
|
+
memory: { total: 128 },
|
|
23
|
+
backend: 'cuda',
|
|
24
|
+
os: { platform: 'linux' }
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// NVIDIA Desktop
|
|
28
|
+
rtx4090: {
|
|
29
|
+
displayName: 'NVIDIA RTX 4090 (Desktop)',
|
|
30
|
+
category: 'nvidia_desktop',
|
|
31
|
+
gpu: { model: 'NVIDIA GeForce RTX 4090', vendor: 'NVIDIA', vram: 24, dedicated: true },
|
|
32
|
+
cpu: { brand: 'AMD Ryzen 9 7950X', cores: 16, physicalCores: 16, speed: 4.5, architecture: 'x86_64' },
|
|
33
|
+
memory: { total: 64 },
|
|
34
|
+
backend: 'cuda',
|
|
35
|
+
os: { platform: 'linux' }
|
|
36
|
+
},
|
|
37
|
+
rtx3090: {
|
|
38
|
+
displayName: 'NVIDIA RTX 3090 (Desktop)',
|
|
39
|
+
category: 'nvidia_desktop',
|
|
40
|
+
gpu: { model: 'NVIDIA GeForce RTX 3090', vendor: 'NVIDIA', vram: 24, dedicated: true },
|
|
41
|
+
cpu: { brand: 'AMD Ryzen 9 5950X', cores: 16, physicalCores: 16, speed: 3.4, architecture: 'x86_64' },
|
|
42
|
+
memory: { total: 64 },
|
|
43
|
+
backend: 'cuda',
|
|
44
|
+
os: { platform: 'linux' }
|
|
45
|
+
},
|
|
46
|
+
rtx4070ti: {
|
|
47
|
+
displayName: 'NVIDIA RTX 4070 Ti (Desktop)',
|
|
48
|
+
category: 'nvidia_desktop',
|
|
49
|
+
gpu: { model: 'NVIDIA GeForce RTX 4070 Ti', vendor: 'NVIDIA', vram: 12, dedicated: true },
|
|
50
|
+
cpu: { brand: 'Intel Core i7-13700K', cores: 16, physicalCores: 16, speed: 3.4, architecture: 'x86_64' },
|
|
51
|
+
memory: { total: 32 },
|
|
52
|
+
backend: 'cuda',
|
|
53
|
+
os: { platform: 'linux' }
|
|
54
|
+
},
|
|
55
|
+
rtx3060: {
|
|
56
|
+
displayName: 'NVIDIA RTX 3060 (Desktop)',
|
|
57
|
+
category: 'nvidia_desktop',
|
|
58
|
+
gpu: { model: 'NVIDIA GeForce RTX 3060', vendor: 'NVIDIA', vram: 12, dedicated: true },
|
|
59
|
+
cpu: { brand: 'Intel Core i5-12600K', cores: 10, physicalCores: 10, speed: 3.7, architecture: 'x86_64' },
|
|
60
|
+
memory: { total: 32 },
|
|
61
|
+
backend: 'cuda',
|
|
62
|
+
os: { platform: 'linux' }
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Apple Silicon
|
|
66
|
+
m4max48: {
|
|
67
|
+
displayName: 'Apple M4 Max 48GB',
|
|
68
|
+
category: 'apple_silicon',
|
|
69
|
+
gpu: { model: 'Apple M4 Max', vendor: 'Apple', vram: 0, dedicated: false, unified: true },
|
|
70
|
+
cpu: { brand: 'Apple M4 Max', cores: 16, physicalCores: 16, speed: 4.5, architecture: 'Apple Silicon' },
|
|
71
|
+
memory: { total: 48 },
|
|
72
|
+
backend: 'metal',
|
|
73
|
+
os: { platform: 'darwin' }
|
|
74
|
+
},
|
|
75
|
+
m4pro24: {
|
|
76
|
+
displayName: 'Apple M4 Pro 24GB',
|
|
77
|
+
category: 'apple_silicon',
|
|
78
|
+
gpu: { model: 'Apple M4 Pro', vendor: 'Apple', vram: 0, dedicated: false, unified: true },
|
|
79
|
+
cpu: { brand: 'Apple M4 Pro', cores: 14, physicalCores: 14, speed: 4.5, architecture: 'Apple Silicon' },
|
|
80
|
+
memory: { total: 24 },
|
|
81
|
+
backend: 'metal',
|
|
82
|
+
os: { platform: 'darwin' }
|
|
83
|
+
},
|
|
84
|
+
m3_16: {
|
|
85
|
+
displayName: 'Apple M3 16GB',
|
|
86
|
+
category: 'apple_silicon',
|
|
87
|
+
gpu: { model: 'Apple M3', vendor: 'Apple', vram: 0, dedicated: false, unified: true },
|
|
88
|
+
cpu: { brand: 'Apple M3', cores: 8, physicalCores: 8, speed: 4.0, architecture: 'Apple Silicon' },
|
|
89
|
+
memory: { total: 16 },
|
|
90
|
+
backend: 'metal',
|
|
91
|
+
os: { platform: 'darwin' }
|
|
92
|
+
},
|
|
93
|
+
m1_16: {
|
|
94
|
+
displayName: 'Apple M1 16GB',
|
|
95
|
+
category: 'apple_silicon',
|
|
96
|
+
gpu: { model: 'Apple M1', vendor: 'Apple', vram: 0, dedicated: false, unified: true },
|
|
97
|
+
cpu: { brand: 'Apple M1', cores: 8, physicalCores: 8, speed: 3.2, architecture: 'Apple Silicon' },
|
|
98
|
+
memory: { total: 16 },
|
|
99
|
+
backend: 'metal',
|
|
100
|
+
os: { platform: 'darwin' }
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// AMD Desktop
|
|
104
|
+
rx7900xtx: {
|
|
105
|
+
displayName: 'AMD RX 7900 XTX (Desktop)',
|
|
106
|
+
category: 'amd_desktop',
|
|
107
|
+
gpu: { model: 'AMD Radeon RX 7900 XTX', vendor: 'AMD', vram: 24, dedicated: true },
|
|
108
|
+
cpu: { brand: 'AMD Ryzen 9 7900X', cores: 12, physicalCores: 12, speed: 4.7, architecture: 'x86_64' },
|
|
109
|
+
memory: { total: 64 },
|
|
110
|
+
backend: 'rocm',
|
|
111
|
+
os: { platform: 'linux' }
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// CPU Only
|
|
115
|
+
cpu_high: {
|
|
116
|
+
displayName: 'CPU Only (32GB RAM)',
|
|
117
|
+
category: 'cpu_only',
|
|
118
|
+
gpu: { model: '', vendor: '', vram: 0, dedicated: false },
|
|
119
|
+
cpu: { brand: 'Intel Core i9-13900K', cores: 24, physicalCores: 24, speed: 3.0, architecture: 'x86_64' },
|
|
120
|
+
memory: { total: 32 },
|
|
121
|
+
backend: 'cpu',
|
|
122
|
+
os: { platform: 'linux' }
|
|
123
|
+
},
|
|
124
|
+
cpu_mid: {
|
|
125
|
+
displayName: 'CPU Only (16GB RAM)',
|
|
126
|
+
category: 'cpu_only',
|
|
127
|
+
gpu: { model: '', vendor: '', vram: 0, dedicated: false },
|
|
128
|
+
cpu: { brand: 'AMD Ryzen 7 5800X', cores: 8, physicalCores: 8, speed: 3.8, architecture: 'x86_64' },
|
|
129
|
+
memory: { total: 16 },
|
|
130
|
+
backend: 'cpu',
|
|
131
|
+
os: { platform: 'linux' }
|
|
132
|
+
},
|
|
133
|
+
cpu_low: {
|
|
134
|
+
displayName: 'CPU Only (8GB RAM)',
|
|
135
|
+
category: 'cpu_only',
|
|
136
|
+
gpu: { model: '', vendor: '', vram: 0, dedicated: false },
|
|
137
|
+
cpu: { brand: 'Intel Core i5-12600K', cores: 10, physicalCores: 10, speed: 3.7, architecture: 'x86_64' },
|
|
138
|
+
memory: { total: 8 },
|
|
139
|
+
backend: 'cpu',
|
|
140
|
+
os: { platform: 'linux' }
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const CATEGORY_LABELS = {
|
|
145
|
+
data_center: 'Data Center',
|
|
146
|
+
nvidia_desktop: 'NVIDIA Desktop',
|
|
147
|
+
apple_silicon: 'Apple Silicon',
|
|
148
|
+
amd_desktop: 'AMD Desktop',
|
|
149
|
+
cpu_only: 'CPU Only'
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
function buildFullHardwareObject(profileKey) {
|
|
153
|
+
const profile = HARDWARE_PROFILES[profileKey];
|
|
154
|
+
if (!profile) return null;
|
|
155
|
+
|
|
156
|
+
const isApple = profile.os.platform === 'darwin';
|
|
157
|
+
const isUnified = Boolean(profile.gpu.unified);
|
|
158
|
+
const totalRAM = profile.memory.total;
|
|
159
|
+
const vram = profile.gpu.vram;
|
|
160
|
+
|
|
161
|
+
// Compute effective memory (matches unified-detector logic)
|
|
162
|
+
const effectiveMemory = isUnified
|
|
163
|
+
? totalRAM
|
|
164
|
+
: (vram > 0 ? vram : Math.round(totalRAM * 0.7));
|
|
165
|
+
|
|
166
|
+
// Get scores from HardwareSpecs where available
|
|
167
|
+
const cpuSpecs = specs.getCPUScore(profile.cpu.brand);
|
|
168
|
+
const gpuSpecs = profile.gpu.model ? specs.getGPUScore(profile.gpu.model) : { score: 0 };
|
|
169
|
+
|
|
170
|
+
// Simulate ~60% free memory
|
|
171
|
+
const freeRAM = Math.round(totalRAM * 0.6);
|
|
172
|
+
const usedRAM = totalRAM - freeRAM;
|
|
173
|
+
|
|
174
|
+
// Build the full hardware object (Shape A - matches HardwareDetector.getSystemInfo() output)
|
|
175
|
+
const hardware = {
|
|
176
|
+
cpu: {
|
|
177
|
+
brand: profile.cpu.brand,
|
|
178
|
+
manufacturer: isApple ? 'Apple' : (profile.cpu.brand.includes('Intel') ? 'Intel' : 'AMD'),
|
|
179
|
+
family: 'Unknown',
|
|
180
|
+
model: 'Unknown',
|
|
181
|
+
speed: profile.cpu.speed,
|
|
182
|
+
speedMax: profile.cpu.speed,
|
|
183
|
+
cores: profile.cpu.cores,
|
|
184
|
+
physicalCores: profile.cpu.physicalCores,
|
|
185
|
+
processors: 1,
|
|
186
|
+
cache: { l1d: 0, l1i: 0, l2: 0, l3: 0 },
|
|
187
|
+
architecture: profile.cpu.architecture,
|
|
188
|
+
score: cpuSpecs.score || 70
|
|
189
|
+
},
|
|
190
|
+
memory: {
|
|
191
|
+
total: totalRAM,
|
|
192
|
+
free: freeRAM,
|
|
193
|
+
used: usedRAM,
|
|
194
|
+
available: freeRAM,
|
|
195
|
+
usagePercent: Math.round((usedRAM / totalRAM) * 100),
|
|
196
|
+
swapTotal: 0,
|
|
197
|
+
swapUsed: 0,
|
|
198
|
+
score: totalRAM >= 64 ? 55 : (totalRAM >= 32 ? 50 : (totalRAM >= 16 ? 40 : 25))
|
|
199
|
+
},
|
|
200
|
+
gpu: {
|
|
201
|
+
model: profile.gpu.model || 'No GPU detected',
|
|
202
|
+
vendor: profile.gpu.vendor || 'Unknown',
|
|
203
|
+
vram: vram,
|
|
204
|
+
vramPerGPU: vram,
|
|
205
|
+
vramDynamic: false,
|
|
206
|
+
dedicated: profile.gpu.dedicated,
|
|
207
|
+
driverVersion: 'Simulated',
|
|
208
|
+
gpuCount: 1,
|
|
209
|
+
isMultiGPU: false,
|
|
210
|
+
all: profile.gpu.model ? [{
|
|
211
|
+
model: profile.gpu.model,
|
|
212
|
+
vram: vram,
|
|
213
|
+
vendor: profile.gpu.vendor
|
|
214
|
+
}] : [],
|
|
215
|
+
displays: 1,
|
|
216
|
+
score: gpuSpecs.score || 0,
|
|
217
|
+
unified: isUnified,
|
|
218
|
+
backend: profile.backend
|
|
219
|
+
},
|
|
220
|
+
system: {
|
|
221
|
+
manufacturer: isApple ? 'Apple' : 'Simulated System',
|
|
222
|
+
model: profile.displayName,
|
|
223
|
+
version: 'Simulated'
|
|
224
|
+
},
|
|
225
|
+
os: {
|
|
226
|
+
platform: profile.os.platform,
|
|
227
|
+
distro: isApple ? 'macOS' : 'Linux',
|
|
228
|
+
release: 'Simulated',
|
|
229
|
+
codename: 'Simulated',
|
|
230
|
+
kernel: 'Simulated',
|
|
231
|
+
arch: profile.cpu.architecture === 'Apple Silicon' ? 'arm64' : 'x64',
|
|
232
|
+
hostname: 'simulated-host',
|
|
233
|
+
logofile: ''
|
|
234
|
+
},
|
|
235
|
+
timestamp: Date.now(),
|
|
236
|
+
|
|
237
|
+
// Shape B - for ScoringEngine / test compatibility
|
|
238
|
+
summary: {
|
|
239
|
+
bestBackend: profile.backend,
|
|
240
|
+
gpuModel: profile.gpu.model,
|
|
241
|
+
effectiveMemory: effectiveMemory,
|
|
242
|
+
systemRAM: totalRAM,
|
|
243
|
+
totalVRAM: vram
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Add CPU capabilities for scoring engine
|
|
248
|
+
hardware.cpu.capabilities = {};
|
|
249
|
+
if (profile.cpu.architecture === 'Apple Silicon') {
|
|
250
|
+
hardware.cpu.capabilities.neon = true;
|
|
251
|
+
} else {
|
|
252
|
+
hardware.cpu.capabilities.avx2 = true;
|
|
253
|
+
if (profile.cpu.cores >= 64) {
|
|
254
|
+
hardware.cpu.capabilities.avx512 = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return hardware;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function inferGpuDetails(gpuName) {
|
|
262
|
+
if (!gpuName) return { model: '', vendor: '', vram: 0, dedicated: false, unified: false, backend: 'cpu', platform: 'linux' };
|
|
263
|
+
|
|
264
|
+
const lower = gpuName.toLowerCase();
|
|
265
|
+
let vendor = 'Unknown';
|
|
266
|
+
let dedicated = true;
|
|
267
|
+
let unified = false;
|
|
268
|
+
let backend = 'cuda';
|
|
269
|
+
let platform = 'linux';
|
|
270
|
+
|
|
271
|
+
if (lower.includes('nvidia') || lower.includes('rtx') || lower.includes('gtx') || lower.includes('geforce')) {
|
|
272
|
+
vendor = 'NVIDIA';
|
|
273
|
+
backend = 'cuda';
|
|
274
|
+
} else if (lower.includes('amd') || lower.includes('radeon') || lower.includes('rx ')) {
|
|
275
|
+
vendor = 'AMD';
|
|
276
|
+
backend = 'rocm';
|
|
277
|
+
} else if (lower.includes('apple') || /\bm[1-9]\b/.test(lower)) {
|
|
278
|
+
vendor = 'Apple';
|
|
279
|
+
backend = 'metal';
|
|
280
|
+
platform = 'darwin';
|
|
281
|
+
dedicated = false;
|
|
282
|
+
unified = true;
|
|
283
|
+
} else if (lower.includes('intel') && (lower.includes('arc') || lower.includes('iris') || lower.includes('uhd'))) {
|
|
284
|
+
vendor = 'Intel';
|
|
285
|
+
backend = 'cpu';
|
|
286
|
+
dedicated = lower.includes('arc');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Normalize model name to match what estimateVRAMFromModel expects
|
|
290
|
+
let model = gpuName;
|
|
291
|
+
if (vendor === 'NVIDIA' && !lower.includes('nvidia')) {
|
|
292
|
+
model = `NVIDIA GeForce ${gpuName}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Use HardwareDetector's VRAM estimation logic
|
|
296
|
+
const HardwareDetector = require('./detector');
|
|
297
|
+
const detector = new HardwareDetector();
|
|
298
|
+
const estimatedVram = detector.estimateVRAMFromModel(model);
|
|
299
|
+
|
|
300
|
+
return { model, vendor, vram: estimatedVram, dedicated, unified, backend, platform };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function inferCpuDetails(cpuName) {
|
|
304
|
+
if (!cpuName) return { brand: 'Unknown CPU', cores: 8, physicalCores: 8, speed: 3.5, architecture: 'x86_64', manufacturer: 'Unknown' };
|
|
305
|
+
|
|
306
|
+
const lower = cpuName.toLowerCase();
|
|
307
|
+
const cpuSpecs = specs.getCPUScore(cpuName);
|
|
308
|
+
let manufacturer = 'Unknown';
|
|
309
|
+
let architecture = 'x86_64';
|
|
310
|
+
|
|
311
|
+
if (lower.includes('apple') || /\bm[1-9]\b/.test(lower)) {
|
|
312
|
+
manufacturer = 'Apple';
|
|
313
|
+
architecture = 'Apple Silicon';
|
|
314
|
+
} else if (lower.includes('intel')) {
|
|
315
|
+
manufacturer = 'Intel';
|
|
316
|
+
} else if (lower.includes('amd') || lower.includes('ryzen') || lower.includes('epyc')) {
|
|
317
|
+
manufacturer = 'AMD';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
brand: cpuName,
|
|
322
|
+
cores: cpuSpecs.cores || 8,
|
|
323
|
+
physicalCores: cpuSpecs.cores || 8,
|
|
324
|
+
speed: 3.5,
|
|
325
|
+
architecture,
|
|
326
|
+
manufacturer
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function buildCustomHardwareObject({ gpu, ram, cpu, vram: overrideVram }) {
|
|
331
|
+
const gpuDetails = inferGpuDetails(gpu);
|
|
332
|
+
const cpuDetails = inferCpuDetails(cpu);
|
|
333
|
+
|
|
334
|
+
const totalRAM = ram || 16;
|
|
335
|
+
const vram = (overrideVram != null && Number.isFinite(overrideVram) && overrideVram > 0)
|
|
336
|
+
? overrideVram : gpuDetails.vram;
|
|
337
|
+
const isApple = gpuDetails.platform === 'darwin' || cpuDetails.architecture === 'Apple Silicon';
|
|
338
|
+
const isUnified = gpuDetails.unified || isApple;
|
|
339
|
+
const platform = isApple ? 'darwin' : 'linux';
|
|
340
|
+
const backend = gpuDetails.backend;
|
|
341
|
+
|
|
342
|
+
const effectiveMemory = isUnified
|
|
343
|
+
? totalRAM
|
|
344
|
+
: (vram > 0 ? vram : Math.round(totalRAM * 0.7));
|
|
345
|
+
|
|
346
|
+
const gpuSpecs = gpuDetails.model ? specs.getGPUScore(gpuDetails.model) : { score: 0 };
|
|
347
|
+
const cpuScore = specs.getCPUScore(cpuDetails.brand);
|
|
348
|
+
const freeRAM = Math.round(totalRAM * 0.6);
|
|
349
|
+
const usedRAM = totalRAM - freeRAM;
|
|
350
|
+
|
|
351
|
+
const displayParts = [];
|
|
352
|
+
if (cpuDetails.brand && cpuDetails.brand !== 'Unknown CPU') displayParts.push(cpuDetails.brand);
|
|
353
|
+
displayParts.push(`${totalRAM}GB RAM`);
|
|
354
|
+
if (gpuDetails.model) displayParts.push(gpuDetails.model);
|
|
355
|
+
const displayName = `Custom: ${displayParts.join(' + ')}`;
|
|
356
|
+
|
|
357
|
+
const hardware = {
|
|
358
|
+
cpu: {
|
|
359
|
+
brand: cpuDetails.brand,
|
|
360
|
+
manufacturer: cpuDetails.manufacturer,
|
|
361
|
+
family: 'Unknown',
|
|
362
|
+
model: 'Unknown',
|
|
363
|
+
speed: cpuDetails.speed,
|
|
364
|
+
speedMax: cpuDetails.speed,
|
|
365
|
+
cores: cpuDetails.cores,
|
|
366
|
+
physicalCores: cpuDetails.physicalCores,
|
|
367
|
+
processors: 1,
|
|
368
|
+
cache: { l1d: 0, l1i: 0, l2: 0, l3: 0 },
|
|
369
|
+
architecture: cpuDetails.architecture,
|
|
370
|
+
score: cpuScore.score || 70
|
|
371
|
+
},
|
|
372
|
+
memory: {
|
|
373
|
+
total: totalRAM,
|
|
374
|
+
free: freeRAM,
|
|
375
|
+
used: usedRAM,
|
|
376
|
+
available: freeRAM,
|
|
377
|
+
usagePercent: Math.round((usedRAM / totalRAM) * 100),
|
|
378
|
+
swapTotal: 0,
|
|
379
|
+
swapUsed: 0,
|
|
380
|
+
score: totalRAM >= 64 ? 55 : (totalRAM >= 32 ? 50 : (totalRAM >= 16 ? 40 : 25))
|
|
381
|
+
},
|
|
382
|
+
gpu: {
|
|
383
|
+
model: gpuDetails.model || 'No GPU detected',
|
|
384
|
+
vendor: gpuDetails.vendor || 'Unknown',
|
|
385
|
+
vram: vram,
|
|
386
|
+
vramPerGPU: vram,
|
|
387
|
+
vramDynamic: false,
|
|
388
|
+
dedicated: gpuDetails.dedicated,
|
|
389
|
+
driverVersion: 'Simulated',
|
|
390
|
+
gpuCount: 1,
|
|
391
|
+
isMultiGPU: false,
|
|
392
|
+
all: gpuDetails.model ? [{
|
|
393
|
+
model: gpuDetails.model,
|
|
394
|
+
vram: vram,
|
|
395
|
+
vendor: gpuDetails.vendor
|
|
396
|
+
}] : [],
|
|
397
|
+
displays: 1,
|
|
398
|
+
score: gpuSpecs.score || 0,
|
|
399
|
+
unified: isUnified,
|
|
400
|
+
backend: backend
|
|
401
|
+
},
|
|
402
|
+
system: {
|
|
403
|
+
manufacturer: isApple ? 'Apple' : 'Simulated System',
|
|
404
|
+
model: displayName,
|
|
405
|
+
version: 'Simulated'
|
|
406
|
+
},
|
|
407
|
+
os: {
|
|
408
|
+
platform: platform,
|
|
409
|
+
distro: isApple ? 'macOS' : 'Linux',
|
|
410
|
+
release: 'Simulated',
|
|
411
|
+
codename: 'Simulated',
|
|
412
|
+
kernel: 'Simulated',
|
|
413
|
+
arch: cpuDetails.architecture === 'Apple Silicon' ? 'arm64' : 'x64',
|
|
414
|
+
hostname: 'simulated-host',
|
|
415
|
+
logofile: ''
|
|
416
|
+
},
|
|
417
|
+
timestamp: Date.now(),
|
|
418
|
+
summary: {
|
|
419
|
+
bestBackend: backend,
|
|
420
|
+
gpuModel: gpuDetails.model,
|
|
421
|
+
effectiveMemory: effectiveMemory,
|
|
422
|
+
systemRAM: totalRAM,
|
|
423
|
+
totalVRAM: vram
|
|
424
|
+
},
|
|
425
|
+
_displayName: displayName
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
hardware.cpu.capabilities = {};
|
|
429
|
+
if (cpuDetails.architecture === 'Apple Silicon') {
|
|
430
|
+
hardware.cpu.capabilities.neon = true;
|
|
431
|
+
} else {
|
|
432
|
+
hardware.cpu.capabilities.avx2 = true;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return hardware;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function getProfile(key) {
|
|
439
|
+
return HARDWARE_PROFILES[key] || null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function getProfileKeys() {
|
|
443
|
+
return Object.keys(HARDWARE_PROFILES);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function getProfilesByCategory() {
|
|
447
|
+
const grouped = {};
|
|
448
|
+
for (const [key, profile] of Object.entries(HARDWARE_PROFILES)) {
|
|
449
|
+
const cat = profile.category;
|
|
450
|
+
if (!grouped[cat]) grouped[cat] = {};
|
|
451
|
+
grouped[cat][key] = profile;
|
|
452
|
+
}
|
|
453
|
+
return grouped;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function listProfiles() {
|
|
457
|
+
const lines = [];
|
|
458
|
+
const grouped = getProfilesByCategory();
|
|
459
|
+
|
|
460
|
+
for (const [category, profiles] of Object.entries(grouped)) {
|
|
461
|
+
const label = CATEGORY_LABELS[category] || category;
|
|
462
|
+
lines.push(`\n ${label}:`);
|
|
463
|
+
for (const [key, profile] of Object.entries(profiles)) {
|
|
464
|
+
const vramLabel = profile.gpu.unified
|
|
465
|
+
? `${profile.memory.total}GB unified`
|
|
466
|
+
: (profile.gpu.vram > 0 ? `${profile.gpu.vram}GB VRAM` : 'No GPU');
|
|
467
|
+
const ramLabel = profile.gpu.unified ? '' : `, ${profile.memory.total}GB RAM`;
|
|
468
|
+
lines.push(` ${key.padEnd(14)} ${profile.displayName.padEnd(38)} ${vramLabel}${ramLabel}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return lines;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
module.exports = {
|
|
476
|
+
HARDWARE_PROFILES,
|
|
477
|
+
buildFullHardwareObject,
|
|
478
|
+
buildCustomHardwareObject,
|
|
479
|
+
getProfile,
|
|
480
|
+
getProfileKeys,
|
|
481
|
+
getProfilesByCategory,
|
|
482
|
+
listProfiles,
|
|
483
|
+
CATEGORY_LABELS
|
|
484
|
+
};
|
package/src/index.js
CHANGED
|
@@ -32,6 +32,21 @@ class LLMChecker {
|
|
|
32
32
|
this.logger = getLogger().createChild('LLMChecker');
|
|
33
33
|
this.verbose = options.verbose !== false; // Default to verbose unless explicitly disabled
|
|
34
34
|
this.progress = null; // Will be initialized when needed
|
|
35
|
+
this._isSimulated = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setSimulatedHardware(hardwareObject) {
|
|
39
|
+
this.hardwareDetector.setSimulatedHardware(hardwareObject);
|
|
40
|
+
this._isSimulated = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
clearSimulatedHardware() {
|
|
44
|
+
this.hardwareDetector.clearSimulatedHardware();
|
|
45
|
+
this._isSimulated = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get isSimulated() {
|
|
49
|
+
return this._isSimulated;
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
async analyze(options = {}) {
|
|
@@ -47,7 +62,10 @@ class LLMChecker {
|
|
|
47
62
|
|
|
48
63
|
// Step 1: Hardware Detection
|
|
49
64
|
if (this.progress) {
|
|
50
|
-
|
|
65
|
+
const detectionLabel = this._isSimulated
|
|
66
|
+
? 'Using simulated hardware profile...'
|
|
67
|
+
: 'Scanning hardware specifications...';
|
|
68
|
+
this.progress.step('System Detection', detectionLabel);
|
|
51
69
|
}
|
|
52
70
|
|
|
53
71
|
const hardware = await this.hardwareDetector.getSystemInfo();
|
package/src/ui/cli-theme.js
CHANGED
|
@@ -48,7 +48,9 @@ const MASCOT_MASK = [
|
|
|
48
48
|
|
|
49
49
|
const DEFAULT_LOOP = true;
|
|
50
50
|
const FRAMES_PER_SECOND = 14;
|
|
51
|
-
|
|
51
|
+
// Security: do not auto-load executable-style banner sources from user-writable folders.
|
|
52
|
+
// External banner loading is opt-in via LLM_CHECKER_BANNER_SOURCE and supports JSON only.
|
|
53
|
+
const DEFAULT_BANNER_SOURCE = null;
|
|
52
54
|
const DEFAULT_TEXT_BANNER_SOURCE = path.join(
|
|
53
55
|
os.homedir(),
|
|
54
56
|
'Desktop',
|
|
@@ -74,80 +76,28 @@ function fitLine(line, width) {
|
|
|
74
76
|
return `${value.slice(0, width - 3)}...`;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let depth = 0;
|
|
81
|
-
let inString = null;
|
|
82
|
-
let escape = false;
|
|
83
|
-
|
|
84
|
-
for (let index = startIndex; index < source.length; index += 1) {
|
|
85
|
-
const char = source[index];
|
|
86
|
-
|
|
87
|
-
if (inString) {
|
|
88
|
-
if (escape) {
|
|
89
|
-
escape = false;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (char === '\\') {
|
|
94
|
-
escape = true;
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (char === inString) {
|
|
99
|
-
inString = null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (char === '"' || char === '\'' || char === '`') {
|
|
106
|
-
inString = char;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (char === openChar) {
|
|
111
|
-
depth += 1;
|
|
112
|
-
} else if (char === closeChar) {
|
|
113
|
-
depth -= 1;
|
|
114
|
-
if (depth === 0) {
|
|
115
|
-
return source.slice(startIndex, index + 1);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return null;
|
|
79
|
+
function isPlainObject(value) {
|
|
80
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
121
81
|
}
|
|
122
82
|
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
const markerIndex = source.indexOf(marker);
|
|
126
|
-
if (markerIndex < 0) return null;
|
|
127
|
-
|
|
128
|
-
const equalsIndex = source.indexOf('=', markerIndex);
|
|
129
|
-
if (equalsIndex < 0) return null;
|
|
130
|
-
|
|
131
|
-
const startIndex = source.indexOf(openChar, equalsIndex);
|
|
132
|
-
if (startIndex < 0) return null;
|
|
133
|
-
|
|
134
|
-
return extractBalanced(source, startIndex, openChar, closeChar);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function evaluateLiteral(literal) {
|
|
138
|
-
if (!literal) return null;
|
|
83
|
+
function parseExternalBannerPayload(source) {
|
|
84
|
+
let parsed = null;
|
|
139
85
|
try {
|
|
140
|
-
|
|
86
|
+
parsed = JSON.parse(source);
|
|
141
87
|
} catch {
|
|
142
88
|
return null;
|
|
143
89
|
}
|
|
144
|
-
}
|
|
145
90
|
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
if (!
|
|
149
|
-
|
|
150
|
-
return
|
|
91
|
+
if (!isPlainObject(parsed)) return null;
|
|
92
|
+
const frames = Array.isArray(parsed.frames) ? parsed.frames : null;
|
|
93
|
+
if (!frames || frames.length === 0) return null;
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
frames,
|
|
97
|
+
themeDark: isPlainObject(parsed.themeDark) ? parsed.themeDark : {},
|
|
98
|
+
themeLight: isPlainObject(parsed.themeLight) ? parsed.themeLight : {},
|
|
99
|
+
canvasWidth: Number.isFinite(parsed.canvasWidth) ? parsed.canvasWidth : null
|
|
100
|
+
};
|
|
151
101
|
}
|
|
152
102
|
|
|
153
103
|
function getLongestFrameLine(frames) {
|
|
@@ -176,6 +126,18 @@ function normalizeExternalFrame(frame, contentWidth, defaultDuration) {
|
|
|
176
126
|
|
|
177
127
|
function loadExternalBanner(sourceFile) {
|
|
178
128
|
const filePath = sourceFile || process.env.LLM_CHECKER_BANNER_SOURCE || DEFAULT_BANNER_SOURCE;
|
|
129
|
+
if (!filePath) return null;
|
|
130
|
+
|
|
131
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
132
|
+
if (extension !== '.json') {
|
|
133
|
+
cachedExternalBanner = {
|
|
134
|
+
filePath,
|
|
135
|
+
mtimeMs: -1,
|
|
136
|
+
payload: null
|
|
137
|
+
};
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
179
141
|
let mtimeMs = -1;
|
|
180
142
|
|
|
181
143
|
try {
|
|
@@ -200,16 +162,8 @@ function loadExternalBanner(sourceFile) {
|
|
|
200
162
|
|
|
201
163
|
try {
|
|
202
164
|
const source = fs.readFileSync(filePath, 'utf8');
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
const lightThemeLiteral = extractAssignedLiteral(source, 'THEME_LIGHT', '{', '}');
|
|
206
|
-
|
|
207
|
-
const frames = evaluateLiteral(framesLiteral);
|
|
208
|
-
const themeDark = evaluateLiteral(darkThemeLiteral);
|
|
209
|
-
const themeLight = evaluateLiteral(lightThemeLiteral);
|
|
210
|
-
const canvasWidth = parseNumericConstant(source, 'CANVAS_WIDTH');
|
|
211
|
-
|
|
212
|
-
if (!Array.isArray(frames) || frames.length === 0) {
|
|
165
|
+
const payload = parseExternalBannerPayload(source);
|
|
166
|
+
if (!payload) {
|
|
213
167
|
cachedExternalBanner = {
|
|
214
168
|
filePath,
|
|
215
169
|
mtimeMs,
|
|
@@ -218,13 +172,6 @@ function loadExternalBanner(sourceFile) {
|
|
|
218
172
|
return null;
|
|
219
173
|
}
|
|
220
174
|
|
|
221
|
-
const payload = {
|
|
222
|
-
frames,
|
|
223
|
-
themeDark: themeDark && typeof themeDark === 'object' ? themeDark : {},
|
|
224
|
-
themeLight: themeLight && typeof themeLight === 'object' ? themeLight : {},
|
|
225
|
-
canvasWidth: Number.isFinite(canvasWidth) ? canvasWidth : null
|
|
226
|
-
};
|
|
227
|
-
|
|
228
175
|
cachedExternalBanner = {
|
|
229
176
|
filePath,
|
|
230
177
|
mtimeMs,
|
package/src/data/CLAUDE.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3464 | 10:03 PM | 🔵 | SQL Database Schema - Indexed Model Repository with Benchmarks | ~555 |
|
|
11
|
-
|
|
12
|
-
### Feb 14, 2026
|
|
13
|
-
|
|
14
|
-
| ID | Time | T | Title | Read |
|
|
15
|
-
|----|------|---|-------|------|
|
|
16
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
17
|
-
</claude-mem-context>
|
package/src/hardware/CLAUDE.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3490 | 10:24 PM | 🔵 | Hardware Detector Cache Implementation - 5-Minute TTL Without Force Refresh Option | ~536 |
|
|
11
|
-
| #3440 | 9:58 PM | 🔵 | Hardware Detection System - Multi-GPU Support with Intelligent Selection | ~611 |
|
|
12
|
-
|
|
13
|
-
### Feb 14, 2026
|
|
14
|
-
|
|
15
|
-
| ID | Time | T | Title | Read |
|
|
16
|
-
|----|------|---|-------|------|
|
|
17
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
18
|
-
</claude-mem-context>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3453 | 10:01 PM | 🔵 | CUDA Detector Implementation - NVIDIA GPU Detection via nvidia-smi | ~497 |
|
|
11
|
-
|
|
12
|
-
### Feb 14, 2026
|
|
13
|
-
|
|
14
|
-
| ID | Time | T | Title | Read |
|
|
15
|
-
|----|------|---|-------|------|
|
|
16
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
17
|
-
</claude-mem-context>
|
package/src/models/CLAUDE.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3442 | 9:59 PM | 🔵 | Static Model Database Structure - Hardcoded LLM Specifications | ~572 |
|
|
11
|
-
|
|
12
|
-
### Feb 13, 2026
|
|
13
|
-
|
|
14
|
-
| ID | Time | T | Title | Read |
|
|
15
|
-
|----|------|---|-------|------|
|
|
16
|
-
| #3699 | 12:05 AM | ✅ | Git Push Consolidated Architecture Changes to GitHub | ~367 |
|
|
17
|
-
|
|
18
|
-
### Feb 14, 2026
|
|
19
|
-
|
|
20
|
-
| ID | Time | T | Title | Read |
|
|
21
|
-
|----|------|---|-------|------|
|
|
22
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
23
|
-
</claude-mem-context>
|
package/src/ollama/CLAUDE.md
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3500 | 10:26 PM | 🔴 | pullModel() Stream Handling Improved - Success Validation Added | ~458 |
|
|
11
|
-
| #3499 | " | 🔴 | Race Condition Fixed in Ollama Availability Cache | ~440 |
|
|
12
|
-
| #3498 | 10:25 PM | 🔵 | testModelPerformance() Timeout Already Fixed | ~418 |
|
|
13
|
-
| #3497 | " | 🔴 | Timeout Fixed in deleteModel() Using AbortController | ~391 |
|
|
14
|
-
| #3496 | " | 🔴 | Timeout Fixed in testConnection() Using AbortController | ~395 |
|
|
15
|
-
| #3495 | " | 🔴 | Fixed unbounded memory growth in native scraper HTTP request handler | ~361 |
|
|
16
|
-
| #3493 | " | 🔴 | Fixed race condition in checkOllamaAvailability() with promise deduplication | ~398 |
|
|
17
|
-
| #3491 | 10:24 PM | 🔴 | Added missing clearTimeout() in testModelPerformance() | ~319 |
|
|
18
|
-
| #3489 | " | 🔴 | Fixed node-fetch timeout handling in testModelPerformance() | ~332 |
|
|
19
|
-
| #3488 | " | 🔴 | Fixed node-fetch timeout handling in testConnection() tags check | ~303 |
|
|
20
|
-
| #3486 | " | 🔴 | Fixed node-fetch timeout handling in getRunningModels() | ~308 |
|
|
21
|
-
| #3484 | 10:23 PM | 🔵 | Ollama Client Timeout Implementation - Mixed Patterns with AbortController | ~554 |
|
|
22
|
-
| #3443 | 9:59 PM | 🔵 | Ollama Native Scraper - Web Scraping with Dual Cache Strategy | ~594 |
|
|
23
|
-
| #3437 | 9:58 PM | 🔵 | Ollama Client Implementation - HTTP API Wrapper with Connection Management | ~605 |
|
|
24
|
-
|
|
25
|
-
### Feb 14, 2026
|
|
26
|
-
|
|
27
|
-
| ID | Time | T | Title | Read |
|
|
28
|
-
|----|------|---|-------|------|
|
|
29
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
30
|
-
</claude-mem-context>
|
package/src/plugins/CLAUDE.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3462 | 10:02 PM | 🔵 | Plugin System Architecture - Hook-Based Extensibility Framework | ~648 |
|
|
11
|
-
|
|
12
|
-
### Feb 14, 2026
|
|
13
|
-
|
|
14
|
-
| ID | Time | T | Title | Read |
|
|
15
|
-
|----|------|---|-------|------|
|
|
16
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
17
|
-
</claude-mem-context>
|
package/src/utils/CLAUDE.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Feb 12, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #3438 | 9:58 PM | 🔵 | Configuration Management System - Comprehensive Settings with Environment Overrides | ~580 |
|
|
11
|
-
|
|
12
|
-
### Feb 14, 2026
|
|
13
|
-
|
|
14
|
-
| ID | Time | T | Title | Read |
|
|
15
|
-
|----|------|---|-------|------|
|
|
16
|
-
| #4339 | 6:49 PM | 🟣 | MCP server implementation and documentation added to llm-checker repository | ~457 |
|
|
17
|
-
</claude-mem-context>
|