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,11 @@
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
+ </claude-mem-context>
@@ -0,0 +1,87 @@
1
+ module.exports = {
2
+ name: 'custom-model-plugin',
3
+ version: '1.0.0',
4
+ description: 'Adds custom models to the LLM Checker database',
5
+ author: 'LLM Checker Team',
6
+ type: 'code',
7
+
8
+ // Custom models to add
9
+ customModels: [
10
+ {
11
+ name: "CustomLlama 5B",
12
+ size: "5B",
13
+ type: "local",
14
+ category: "medium",
15
+ requirements: {
16
+ ram: 6,
17
+ vram: 3,
18
+ cpu_cores: 4,
19
+ storage: 5
20
+ },
21
+ frameworks: ["ollama", "llama.cpp"],
22
+ quantization: ["Q4_0", "Q4_K_M", "Q5_0"],
23
+ performance: {
24
+ speed: "medium",
25
+ quality: "very_good",
26
+ context_length: 8192
27
+ },
28
+ installation: {
29
+ ollama: "ollama pull custom-llama:5b",
30
+ description: "Custom trained Llama model"
31
+ },
32
+ specialization: "custom",
33
+ languages: ["en"],
34
+ year: 2024
35
+ }
36
+ ],
37
+
38
+ async initialize(pluginManager) {
39
+ pluginManager.logger.info('Custom Model Plugin initialized');
40
+ },
41
+
42
+ hooks: {
43
+ afterModelAnalysis: async function(data, pluginManager) {
44
+ // Add custom models to the analysis results
45
+ if (data.models) {
46
+ data.models.push(...this.customModels);
47
+ pluginManager.logger.debug(`Added ${this.customModels.length} custom models`);
48
+ }
49
+ return data;
50
+ },
51
+
52
+ beforeGenerateReport: async function(data, pluginManager) {
53
+ // Add note about custom models in reports
54
+ if (data.recommendations) {
55
+ data.recommendations.push('Custom models available via plugin');
56
+ }
57
+ return data;
58
+ }
59
+ },
60
+
61
+ async execute(action, ...args) {
62
+ switch (action) {
63
+ case 'listCustomModels':
64
+ return this.customModels;
65
+
66
+ case 'addCustomModel':
67
+ const [model] = args;
68
+ if (this.validateCustomModel(model)) {
69
+ this.customModels.push(model);
70
+ return { success: true, message: 'Custom model added' };
71
+ }
72
+ return { success: false, message: 'Invalid model format' };
73
+
74
+ default:
75
+ return { success: false, message: 'Unknown action' };
76
+ }
77
+ },
78
+
79
+ validateCustomModel(model) {
80
+ const required = ['name', 'size', 'type', 'category', 'requirements'];
81
+ return required.every(field => model.hasOwnProperty(field));
82
+ },
83
+
84
+ cleanup() {
85
+ console.log('Custom Model Plugin cleaned up');
86
+ }
87
+ };
@@ -0,0 +1,295 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { getLogger } = require('../utils/logger');
4
+
5
+ class PluginManager {
6
+ constructor(options = {}) {
7
+ this.pluginsDir = options.pluginsDir || path.join(process.cwd(), 'plugins');
8
+ this.plugins = new Map();
9
+ this.hooks = new Map();
10
+ this.logger = getLogger().createChild('PluginManager');
11
+ this.enabled = options.enabled !== false;
12
+ }
13
+
14
+ async loadPlugins() {
15
+ if (!this.enabled) {
16
+ this.logger.debug('Plugin system disabled');
17
+ return;
18
+ }
19
+
20
+ if (!fs.existsSync(this.pluginsDir)) {
21
+ this.logger.debug('Plugins directory not found, skipping plugin loading');
22
+ return;
23
+ }
24
+
25
+ try {
26
+ const pluginFiles = fs.readdirSync(this.pluginsDir)
27
+ .filter(file => file.endsWith('.js') || file.endsWith('.json'));
28
+
29
+ for (const file of pluginFiles) {
30
+ await this.loadPlugin(path.join(this.pluginsDir, file));
31
+ }
32
+
33
+ this.logger.info(`Loaded ${this.plugins.size} plugins`);
34
+ } catch (error) {
35
+ this.logger.error('Failed to load plugins', { error });
36
+ }
37
+ }
38
+
39
+ async loadPlugin(pluginPath) {
40
+ try {
41
+ const pluginName = path.basename(pluginPath, path.extname(pluginPath));
42
+
43
+ // Security: Ensure plugin path is within the plugins directory
44
+ const resolvedPath = path.resolve(pluginPath);
45
+ const resolvedPluginsDir = path.resolve(this.pluginsDir);
46
+ if (!resolvedPath.startsWith(resolvedPluginsDir + path.sep)) {
47
+ this.logger.warn(`Blocked plugin outside plugins directory: ${pluginPath}`);
48
+ return false;
49
+ }
50
+
51
+ // Load plugin configuration or code
52
+ // WARNING: Code plugins execute with full Node.js permissions.
53
+ // Only load plugins from trusted sources.
54
+ let plugin;
55
+ if (pluginPath.endsWith('.json')) {
56
+ plugin = JSON.parse(fs.readFileSync(pluginPath, 'utf8'));
57
+ plugin.type = 'config';
58
+ } else {
59
+ plugin = require(resolvedPath);
60
+ plugin.type = 'code';
61
+ }
62
+
63
+ // Validate plugin
64
+ if (!this.validatePlugin(plugin)) {
65
+ this.logger.warn(`Invalid plugin: ${pluginName}`);
66
+ return false;
67
+ }
68
+
69
+ // Initialize plugin
70
+ if (plugin.initialize && typeof plugin.initialize === 'function') {
71
+ await plugin.initialize(this);
72
+ }
73
+
74
+ // Register plugin
75
+ this.plugins.set(pluginName, {
76
+ ...plugin,
77
+ name: pluginName,
78
+ path: pluginPath,
79
+ loaded: true
80
+ });
81
+
82
+ // Register hooks
83
+ if (plugin.hooks) {
84
+ this.registerHooks(pluginName, plugin.hooks);
85
+ }
86
+
87
+ this.logger.debug(`Plugin loaded: ${pluginName}`, {
88
+ data: { type: plugin.type, version: plugin.version }
89
+ });
90
+
91
+ return true;
92
+ } catch (error) {
93
+ this.logger.error(`Failed to load plugin: ${pluginPath}`, { error });
94
+ return false;
95
+ }
96
+ }
97
+
98
+ validatePlugin(plugin) {
99
+ // Basic plugin validation
100
+ if (!plugin.name || !plugin.version) {
101
+ return false;
102
+ }
103
+
104
+ if (plugin.type === 'code' && !plugin.execute && !plugin.hooks) {
105
+ return false;
106
+ }
107
+
108
+ return true;
109
+ }
110
+
111
+ registerHooks(pluginName, hooks) {
112
+ for (const [hookName, hookFunction] of Object.entries(hooks)) {
113
+ if (!this.hooks.has(hookName)) {
114
+ this.hooks.set(hookName, []);
115
+ }
116
+
117
+ this.hooks.get(hookName).push({
118
+ plugin: pluginName,
119
+ function: hookFunction
120
+ });
121
+ }
122
+ }
123
+
124
+ async executeHook(hookName, data = {}) {
125
+ if (!this.hooks.has(hookName)) {
126
+ return data;
127
+ }
128
+
129
+ let result = data;
130
+ const hookFunctions = this.hooks.get(hookName);
131
+
132
+ for (const hook of hookFunctions) {
133
+ try {
134
+ this.logger.trace(`Executing hook: ${hookName} from plugin: ${hook.plugin}`);
135
+ result = await hook.function(result, this) || result;
136
+ } catch (error) {
137
+ this.logger.error(`Hook execution failed`, {
138
+ data: { hook: hookName, plugin: hook.plugin },
139
+ error
140
+ });
141
+ }
142
+ }
143
+
144
+ return result;
145
+ }
146
+
147
+ async executePlugin(pluginName, ...args) {
148
+ const plugin = this.plugins.get(pluginName);
149
+
150
+ if (!plugin) {
151
+ throw new Error(`Plugin not found: ${pluginName}`);
152
+ }
153
+
154
+ if (!plugin.execute || typeof plugin.execute !== 'function') {
155
+ throw new Error(`Plugin ${pluginName} is not executable`);
156
+ }
157
+
158
+ try {
159
+ this.logger.debug(`Executing plugin: ${pluginName}`);
160
+ return await plugin.execute(...args);
161
+ } catch (error) {
162
+ this.logger.error(`Plugin execution failed: ${pluginName}`, { error });
163
+ throw error;
164
+ }
165
+ }
166
+
167
+ getPlugin(name) {
168
+ return this.plugins.get(name);
169
+ }
170
+
171
+ listPlugins() {
172
+ return Array.from(this.plugins.values()).map(plugin => ({
173
+ name: plugin.name,
174
+ version: plugin.version,
175
+ description: plugin.description,
176
+ type: plugin.type,
177
+ loaded: plugin.loaded
178
+ }));
179
+ }
180
+
181
+ unloadPlugin(name) {
182
+ const plugin = this.plugins.get(name);
183
+
184
+ if (!plugin) {
185
+ return false;
186
+ }
187
+
188
+ // Remove hooks
189
+ for (const [hookName, hooks] of this.hooks.entries()) {
190
+ const filtered = hooks.filter(hook => hook.plugin !== name);
191
+ if (filtered.length === 0) {
192
+ this.hooks.delete(hookName);
193
+ } else {
194
+ this.hooks.set(hookName, filtered);
195
+ }
196
+ }
197
+
198
+ // Call cleanup if available
199
+ if (plugin.cleanup && typeof plugin.cleanup === 'function') {
200
+ try {
201
+ plugin.cleanup();
202
+ } catch (error) {
203
+ this.logger.error(`Plugin cleanup failed: ${name}`, { error });
204
+ }
205
+ }
206
+
207
+ this.plugins.delete(name);
208
+ this.logger.debug(`Plugin unloaded: ${name}`);
209
+
210
+ return true;
211
+ }
212
+
213
+ reloadPlugin(name) {
214
+ const plugin = this.plugins.get(name);
215
+
216
+ if (!plugin) {
217
+ return false;
218
+ }
219
+
220
+ const pluginPath = plugin.path;
221
+ this.unloadPlugin(name);
222
+
223
+ // Clear require cache
224
+ delete require.cache[require.resolve(pluginPath)];
225
+
226
+ return this.loadPlugin(pluginPath);
227
+ }
228
+
229
+ // Built-in hooks for extending LLM Checker functionality
230
+ getAvailableHooks() {
231
+ return [
232
+ 'beforeHardwareDetection',
233
+ 'afterHardwareDetection',
234
+ 'beforeModelAnalysis',
235
+ 'afterModelAnalysis',
236
+ 'beforeOllamaOperation',
237
+ 'afterOllamaOperation',
238
+ 'beforeFormatOutput',
239
+ 'afterFormatOutput',
240
+ 'beforeGenerateReport',
241
+ 'afterGenerateReport'
242
+ ];
243
+ }
244
+
245
+ // Plugin development utilities
246
+ createPluginTemplate(name, type = 'code') {
247
+ const template = {
248
+ name: name,
249
+ version: '1.0.0',
250
+ description: `Plugin: ${name}`,
251
+ author: 'Your Name',
252
+ type: type
253
+ };
254
+
255
+ if (type === 'code') {
256
+ template.initialize = async function(pluginManager) {
257
+ // Plugin initialization code
258
+ console.log(`Plugin ${name} initialized`);
259
+ };
260
+
261
+ template.execute = async function(...args) {
262
+ // Plugin execution code
263
+ console.log(`Plugin ${name} executed with args:`, args);
264
+ return { success: true };
265
+ };
266
+
267
+ template.hooks = {
268
+ beforeModelAnalysis: async function(data, pluginManager) {
269
+ // Hook implementation
270
+ console.log(`${name} hook: beforeModelAnalysis`);
271
+ return data;
272
+ }
273
+ };
274
+
275
+ template.cleanup = function() {
276
+ // Cleanup code
277
+ console.log(`Plugin ${name} cleaned up`);
278
+ };
279
+ }
280
+
281
+ return template;
282
+ }
283
+
284
+ savePluginTemplate(name, outputPath, type = 'code') {
285
+ const template = this.createPluginTemplate(name, type);
286
+ const content = type === 'code' ?
287
+ `module.exports = ${JSON.stringify(template, null, 2)};` :
288
+ JSON.stringify(template, null, 2);
289
+
290
+ fs.writeFileSync(outputPath, content, 'utf8');
291
+ this.logger.info(`Plugin template created: ${outputPath}`);
292
+ }
293
+ }
294
+
295
+ module.exports = PluginManager;
@@ -0,0 +1,11 @@
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
+ </claude-mem-context>