llm-checker 3.5.11 → 3.5.13
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/README.md +83 -17
- package/bin/cli.js +40 -0
- package/bin/enhanced_cli.js +384 -35
- package/package.json +2 -1
- package/src/ai/model-selector.js +47 -16
- package/src/ai/multi-objective-selector.js +55 -9
- package/src/data/model-database.js +92 -1
- package/src/data/seed/README.md +8 -0
- package/src/data/seed/models.db +0 -0
- package/src/hardware/backends/rocm-detector.js +469 -68
- package/src/hardware/unified-detector.js +69 -18
- package/src/index.js +59 -8
- package/src/models/ai-check-selector.js +27 -2
- package/src/models/deterministic-selector.js +84 -7
- package/src/ollama/client.js +121 -0
- package/src/ollama/enhanced-scraper.js +40 -26
- package/src/ollama/native-scraper.js +52 -27
- package/src/ui/cli-theme.js +139 -24
- package/src/ui/interactive-panel.js +1 -18
- package/src/utils/verbose-progress.js +144 -187
|
@@ -2,8 +2,8 @@ const chalk = require('chalk');
|
|
|
2
2
|
const ora = require('ora');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Minimal activity indicator used while analysis is running.
|
|
6
|
+
* It intentionally keeps output to one animated line, then clears before results.
|
|
7
7
|
*/
|
|
8
8
|
class VerboseProgress {
|
|
9
9
|
constructor(enabled = true) {
|
|
@@ -12,255 +12,212 @@ class VerboseProgress {
|
|
|
12
12
|
this.totalSteps = 0;
|
|
13
13
|
this.operationTitle = '';
|
|
14
14
|
this.startTime = null;
|
|
15
|
-
this.stepTimes = [];
|
|
16
15
|
this.currentSpinner = null;
|
|
17
|
-
this.
|
|
16
|
+
this.animationTimer = null;
|
|
17
|
+
this.currentVerb = 'thinking';
|
|
18
|
+
this.currentDetail = '';
|
|
19
|
+
this.colorPhase = 0;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
/**
|
|
21
|
-
* Inicia una nueva operación con múltiples pasos
|
|
22
|
-
*/
|
|
23
22
|
startOperation(title, totalSteps) {
|
|
24
|
-
if (!this.enabled) return;
|
|
25
|
-
|
|
23
|
+
if (!this.enabled) return this;
|
|
24
|
+
|
|
26
25
|
this.operationTitle = title;
|
|
27
26
|
this.totalSteps = totalSteps;
|
|
28
27
|
this.currentStep = 0;
|
|
29
28
|
this.startTime = Date.now();
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
this.currentVerb = 'thinking';
|
|
30
|
+
this.currentDetail = '';
|
|
31
|
+
|
|
32
|
+
if (!this.shouldAnimate()) return this;
|
|
33
|
+
|
|
34
|
+
this.currentSpinner = ora({
|
|
35
|
+
text: this.renderActivityText(),
|
|
36
|
+
spinner: 'dots'
|
|
37
|
+
}).start();
|
|
38
|
+
|
|
39
|
+
this.animationTimer = setInterval(() => {
|
|
40
|
+
this.colorPhase += 1;
|
|
41
|
+
this.updateSpinner();
|
|
42
|
+
}, 120);
|
|
43
|
+
|
|
44
|
+
return this;
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
/**
|
|
36
|
-
* Avanza al siguiente paso de la operación
|
|
37
|
-
*/
|
|
38
47
|
step(description, details = null) {
|
|
39
|
-
if (!this.enabled) return;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.currentStep++;
|
|
47
|
-
this.stepStartTime = Date.now();
|
|
48
|
-
|
|
49
|
-
// Crear indicador de progreso visual
|
|
50
|
-
const progress = this.createProgressBar();
|
|
51
|
-
const stepIndicator = chalk.cyan(`[${this.currentStep}/${this.totalSteps}]`);
|
|
52
|
-
|
|
53
|
-
// Mostrar el paso actual
|
|
54
|
-
console.log(`\n${progress} ${stepIndicator} ${chalk.white.bold(description)}`);
|
|
55
|
-
|
|
56
|
-
if (details) {
|
|
57
|
-
console.log(` ${chalk.gray('└─ ' + details)}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Crear spinner para este paso (disabled to fix UI issues)
|
|
61
|
-
this.currentSpinner = null;
|
|
62
|
-
|
|
63
|
-
this.stepTimes.push(this.stepStartTime);
|
|
64
|
-
|
|
48
|
+
if (!this.enabled) return this;
|
|
49
|
+
|
|
50
|
+
this.currentStep += 1;
|
|
51
|
+
this.currentVerb = this.verbForStep(description);
|
|
52
|
+
this.currentDetail = details || description || '';
|
|
53
|
+
this.updateSpinner();
|
|
54
|
+
|
|
65
55
|
return this;
|
|
66
56
|
}
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const connector = isLast ? '└─' : '├─';
|
|
75
|
-
const elapsed = this.getStepElapsedTime();
|
|
76
|
-
console.log(` ${chalk.gray(connector)} ${description} ${chalk.dim(`(${elapsed})`)}`);
|
|
77
|
-
|
|
58
|
+
substep(description) {
|
|
59
|
+
if (!this.enabled) return this;
|
|
60
|
+
|
|
61
|
+
this.currentDetail = description || this.currentDetail;
|
|
62
|
+
this.updateSpinner();
|
|
63
|
+
|
|
78
64
|
return this;
|
|
79
65
|
}
|
|
80
66
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (result) {
|
|
88
|
-
console.log(` ${chalk.green(result)}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Mostrar timing
|
|
92
|
-
const elapsed = this.getStepElapsedTime();
|
|
93
|
-
console.log(` ${chalk.dim(`└─ ${elapsed}`)}`);
|
|
94
|
-
|
|
67
|
+
stepComplete(result = null) {
|
|
68
|
+
if (!this.enabled) return this;
|
|
69
|
+
|
|
70
|
+
if (result) this.currentDetail = result;
|
|
71
|
+
this.updateSpinner();
|
|
72
|
+
|
|
95
73
|
return this;
|
|
96
74
|
}
|
|
97
75
|
|
|
98
|
-
/**
|
|
99
|
-
* Marca el paso actual como fallido
|
|
100
|
-
*/
|
|
101
76
|
stepFail(error = null) {
|
|
102
|
-
if (!this.enabled) return;
|
|
103
|
-
|
|
104
|
-
if (error)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
77
|
+
if (!this.enabled) return this;
|
|
78
|
+
|
|
79
|
+
if (error) this.currentDetail = error;
|
|
80
|
+
this.updateSpinner();
|
|
81
|
+
|
|
108
82
|
return this;
|
|
109
83
|
}
|
|
110
84
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const prefix = indent ? ' ' : '';
|
|
118
|
-
console.log(`${prefix}${chalk.gray(message)}`);
|
|
119
|
-
|
|
85
|
+
info(message) {
|
|
86
|
+
if (!this.enabled) return this;
|
|
87
|
+
|
|
88
|
+
this.currentDetail = message || this.currentDetail;
|
|
89
|
+
this.updateSpinner();
|
|
90
|
+
|
|
120
91
|
return this;
|
|
121
92
|
}
|
|
122
93
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const prefix = indent ? ' ' : '';
|
|
130
|
-
console.log(`${prefix}${chalk.yellow(message)}`);
|
|
131
|
-
|
|
94
|
+
warn(message) {
|
|
95
|
+
if (!this.enabled) return this;
|
|
96
|
+
|
|
97
|
+
this.currentDetail = message || this.currentDetail;
|
|
98
|
+
this.updateSpinner();
|
|
99
|
+
|
|
132
100
|
return this;
|
|
133
101
|
}
|
|
134
102
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
103
|
+
found(message, count = null) {
|
|
104
|
+
if (!this.enabled) return this;
|
|
105
|
+
|
|
106
|
+
const countStr = count !== null ? ` (${count})` : '';
|
|
107
|
+
this.currentDetail = `${message || ''}${countStr}`;
|
|
108
|
+
this.updateSpinner();
|
|
109
|
+
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
complete() {
|
|
114
|
+
if (!this.enabled) return this;
|
|
115
|
+
|
|
116
|
+
this.stopSpinner();
|
|
117
|
+
|
|
145
118
|
return this;
|
|
146
119
|
}
|
|
147
120
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
complete(summary = null) {
|
|
152
|
-
if (!this.enabled) return;
|
|
153
|
-
|
|
154
|
-
// Finalizar spinner si existe (disabled)
|
|
121
|
+
fail(error = null) {
|
|
122
|
+
if (!this.enabled) return this;
|
|
123
|
+
|
|
155
124
|
if (this.currentSpinner) {
|
|
156
|
-
|
|
125
|
+
this.currentSpinner.fail(error ? `failed: ${error}` : 'failed');
|
|
157
126
|
this.currentSpinner = null;
|
|
158
127
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
163
|
-
|
|
164
|
-
if (summary) {
|
|
165
|
-
console.log(chalk.green.bold(`${this.operationTitle} complete!`));
|
|
166
|
-
console.log(chalk.gray(` ${summary}`));
|
|
167
|
-
} else {
|
|
168
|
-
console.log(chalk.green.bold(`Operation complete!`));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
console.log(chalk.dim(` Total time: ${totalTime}`));
|
|
172
|
-
console.log('');
|
|
173
|
-
|
|
128
|
+
this.clearTimer();
|
|
129
|
+
|
|
174
130
|
return this;
|
|
175
131
|
}
|
|
176
132
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
133
|
+
shouldAnimate() {
|
|
134
|
+
return Boolean(
|
|
135
|
+
process.stdout.isTTY &&
|
|
136
|
+
process.env.CI !== 'true' &&
|
|
137
|
+
process.env.LLM_CHECKER_DISABLE_ANIMATION !== '1'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
verbForStep(description = '') {
|
|
142
|
+
const text = String(description).toLowerCase();
|
|
143
|
+
|
|
144
|
+
if (text.includes('system') || text.includes('hardware')) return 'scanning';
|
|
145
|
+
if (text.includes('database')) return 'syncing';
|
|
146
|
+
if (text.includes('model analysis')) return 'matching';
|
|
147
|
+
if (text.includes('ollama')) return 'checking';
|
|
148
|
+
if (text.includes('filter')) return 'filtering';
|
|
149
|
+
if (text.includes('compatibility')) return 'matching';
|
|
150
|
+
if (text.includes('performance')) return 'estimating';
|
|
151
|
+
if (text.includes('recommend')) return 'ranking';
|
|
152
|
+
|
|
153
|
+
return 'thinking';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
renderActivityText() {
|
|
157
|
+
const dots = '.'.repeat((this.colorPhase % 3) + 1);
|
|
158
|
+
const verb = this.colorize(`${this.currentVerb}${dots}`);
|
|
159
|
+
const detail = this.currentDetail ? ` ${chalk.dim(this.currentDetail)}` : '';
|
|
160
|
+
|
|
161
|
+
return `${verb}${detail}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
colorize(text) {
|
|
165
|
+
const palette = ['#38BDF8', '#60A5FA', '#818CF8', '#A78BFA', '#22D3EE'];
|
|
166
|
+
return String(text)
|
|
167
|
+
.split('')
|
|
168
|
+
.map((char, index) => chalk.hex(palette[(index + this.colorPhase) % palette.length])(char))
|
|
169
|
+
.join('');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
updateSpinner() {
|
|
173
|
+
if (!this.currentSpinner) return;
|
|
174
|
+
|
|
175
|
+
this.currentSpinner.text = this.renderActivityText();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
stopSpinner() {
|
|
184
179
|
if (this.currentSpinner) {
|
|
185
|
-
|
|
180
|
+
this.currentSpinner.stop();
|
|
186
181
|
this.currentSpinner = null;
|
|
187
182
|
}
|
|
188
|
-
|
|
189
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
190
|
-
console.log(chalk.red.bold(`Operation failed!`));
|
|
191
|
-
|
|
192
|
-
if (error) {
|
|
193
|
-
console.log(chalk.red(` ${error}`));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
console.log('');
|
|
197
|
-
|
|
198
|
-
return this;
|
|
183
|
+
this.clearTimer();
|
|
199
184
|
}
|
|
200
185
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const percentage = (this.currentStep / this.totalSteps);
|
|
208
|
-
const filledBars = Math.floor(percentage * 20);
|
|
209
|
-
const emptyBars = 20 - filledBars;
|
|
210
|
-
|
|
211
|
-
const filled = chalk.cyan('█'.repeat(filledBars));
|
|
212
|
-
const empty = chalk.gray('░'.repeat(emptyBars));
|
|
213
|
-
|
|
214
|
-
return `${filled}${empty} ${Math.round(percentage * 100)}%`;
|
|
186
|
+
clearTimer() {
|
|
187
|
+
if (this.animationTimer) {
|
|
188
|
+
clearInterval(this.animationTimer);
|
|
189
|
+
this.animationTimer = null;
|
|
190
|
+
}
|
|
215
191
|
}
|
|
216
192
|
|
|
217
|
-
/**
|
|
218
|
-
* Obtiene el tiempo transcurrido desde el inicio de la operación
|
|
219
|
-
*/
|
|
220
193
|
getTotalElapsedTime() {
|
|
221
194
|
if (!this.startTime) return '0ms';
|
|
222
|
-
|
|
195
|
+
|
|
223
196
|
const elapsed = Date.now() - this.startTime;
|
|
224
|
-
|
|
197
|
+
|
|
225
198
|
if (elapsed < 1000) return `${elapsed}ms`;
|
|
226
199
|
if (elapsed < 60000) return `${(elapsed / 1000).toFixed(1)}s`;
|
|
227
|
-
|
|
200
|
+
|
|
228
201
|
const minutes = Math.floor(elapsed / 60000);
|
|
229
202
|
const seconds = Math.floor((elapsed % 60000) / 1000);
|
|
230
203
|
return `${minutes}m ${seconds}s`;
|
|
231
204
|
}
|
|
232
205
|
|
|
233
|
-
/**
|
|
234
|
-
* Obtiene el tiempo transcurrido desde el último paso
|
|
235
|
-
*/
|
|
236
206
|
getElapsedTime() {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const lastStepTime = this.stepTimes[this.stepTimes.length - 1];
|
|
240
|
-
const elapsed = Date.now() - lastStepTime;
|
|
241
|
-
|
|
242
|
-
if (elapsed < 1000) return `${elapsed}ms`;
|
|
243
|
-
return `${(elapsed / 1000).toFixed(1)}s`;
|
|
207
|
+
return this.getTotalElapsedTime();
|
|
244
208
|
}
|
|
245
209
|
|
|
246
|
-
/**
|
|
247
|
-
* Obtiene el tiempo transcurrido desde el inicio del paso actual
|
|
248
|
-
*/
|
|
249
210
|
getStepElapsedTime() {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return `${(elapsed / 1000).toFixed(1)}s`;
|
|
211
|
+
return this.getTotalElapsedTime();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
createProgressBar() {
|
|
215
|
+
return '';
|
|
256
216
|
}
|
|
257
217
|
|
|
258
|
-
/**
|
|
259
|
-
* Método estático para crear una instancia rápida
|
|
260
|
-
*/
|
|
261
218
|
static create(enabled = true) {
|
|
262
219
|
return new VerboseProgress(enabled);
|
|
263
220
|
}
|
|
264
221
|
}
|
|
265
222
|
|
|
266
|
-
module.exports = VerboseProgress;
|
|
223
|
+
module.exports = VerboseProgress;
|