pikakit 1.0.7 → 1.0.9
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 +10 -6
- package/bin/kit.mjs +6 -2
- package/bin/lib/commands/help.js +13 -8
- package/bin/lib/config.js +4 -2
- package/lib/agent-cli/lib/ab-testing.js +508 -0
- package/lib/agent-cli/lib/causality-engine.js +623 -0
- package/lib/agent-cli/lib/dashboard-data.js +365 -0
- package/lib/agent-cli/lib/fix.js +1 -1
- package/lib/agent-cli/lib/metrics-collector.js +523 -0
- package/lib/agent-cli/lib/metrics-schema.js +410 -0
- package/lib/agent-cli/lib/precision-skill-generator.js +584 -0
- package/lib/agent-cli/lib/recall.js +1 -1
- package/lib/agent-cli/lib/reinforcement.js +610 -0
- package/lib/agent-cli/lib/ui/index.js +37 -14
- package/package.json +4 -2
- package/lib/agent-cli/lib/auto-learn.js +0 -319
- package/lib/agent-cli/scripts/adaptive_engine.js +0 -381
- package/lib/agent-cli/scripts/error_sensor.js +0 -565
- package/lib/agent-cli/scripts/learn_from_failure.js +0 -225
- package/lib/agent-cli/scripts/pattern_analyzer.js +0 -781
- package/lib/agent-cli/scripts/skill_injector.js +0 -387
- package/lib/agent-cli/scripts/success_sensor.js +0 -500
- package/lib/agent-cli/scripts/user_correction_sensor.js +0 -426
- package/lib/agent-cli/services/auto-learn-service.js +0 -247
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoLearn v6.0 - Metrics Collector
|
|
3
|
+
*
|
|
4
|
+
* Collects, stores, and retrieves metrics for Dashboard.
|
|
5
|
+
* All metrics are measurable and tracked over time.
|
|
6
|
+
*
|
|
7
|
+
* @version 6.0.0
|
|
8
|
+
* @author PikaKit
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { METRICS_SCHEMA, getAllMetricIds, validateMetric } from './metrics-schema.js';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// CONFIGURATION
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const METRICS_DIR = path.join(process.cwd(), '.agent', 'knowledge');
|
|
20
|
+
const METRICS_FILE = path.join(METRICS_DIR, 'autolearn-metrics.json');
|
|
21
|
+
const HISTORY_FILE = path.join(METRICS_DIR, 'metrics-history.json');
|
|
22
|
+
|
|
23
|
+
// Maximum history entries per metric (30 days of hourly data)
|
|
24
|
+
const MAX_HISTORY_ENTRIES = 720;
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// DATA STRUCTURES
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initial metrics state
|
|
32
|
+
*/
|
|
33
|
+
function createInitialMetrics() {
|
|
34
|
+
const metrics = {
|
|
35
|
+
version: '6.0.0',
|
|
36
|
+
createdAt: new Date().toISOString(),
|
|
37
|
+
updatedAt: new Date().toISOString(),
|
|
38
|
+
|
|
39
|
+
// Raw counters (used to calculate rates)
|
|
40
|
+
counters: {
|
|
41
|
+
total_tasks: 0,
|
|
42
|
+
successful_tasks: 0,
|
|
43
|
+
failed_tasks: 0,
|
|
44
|
+
total_errors: 0,
|
|
45
|
+
repeated_errors: 0,
|
|
46
|
+
no_retry_tasks: 0,
|
|
47
|
+
manual_fixes: 0,
|
|
48
|
+
true_positives: 0,
|
|
49
|
+
false_positives: 0,
|
|
50
|
+
false_negatives: 0,
|
|
51
|
+
total_alerts: 0,
|
|
52
|
+
tasks_with_skill: 0,
|
|
53
|
+
tasks_helped_by_skill: 0,
|
|
54
|
+
tasks_where_skill_applied: 0,
|
|
55
|
+
auto_skills_created: 0,
|
|
56
|
+
skills_pruned: 0,
|
|
57
|
+
total_ab_tests: 0,
|
|
58
|
+
ab_tests_with_winner: 0,
|
|
59
|
+
new_patterns_this_week: 0,
|
|
60
|
+
baseline_errors: 0,
|
|
61
|
+
current_errors: 0,
|
|
62
|
+
total_time_saved_seconds: 0,
|
|
63
|
+
total_skill_creation_time_seconds: 0
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Aggregated values
|
|
67
|
+
aggregates: {
|
|
68
|
+
total_task_duration_seconds: 0,
|
|
69
|
+
pattern_confidence_sum: 0,
|
|
70
|
+
pattern_count: 0
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// Calculated KPIs (updated after each event)
|
|
74
|
+
kpis: {},
|
|
75
|
+
|
|
76
|
+
// Weekly snapshots for trend analysis
|
|
77
|
+
weekly: {
|
|
78
|
+
current_week_start: getWeekStart().toISOString(),
|
|
79
|
+
last_week_success_rate: null,
|
|
80
|
+
this_week_success_rate: null
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Initialize all KPIs to 0
|
|
85
|
+
getAllMetricIds().forEach(id => {
|
|
86
|
+
metrics.kpis[id] = 0;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return metrics;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get start of current week (Monday)
|
|
94
|
+
*/
|
|
95
|
+
function getWeekStart() {
|
|
96
|
+
const now = new Date();
|
|
97
|
+
const day = now.getDay();
|
|
98
|
+
const diff = now.getDate() - day + (day === 0 ? -6 : 1);
|
|
99
|
+
const weekStart = new Date(now.setDate(diff));
|
|
100
|
+
weekStart.setHours(0, 0, 0, 0);
|
|
101
|
+
return weekStart;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// STORAGE OPERATIONS
|
|
106
|
+
// ============================================================================
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Load metrics from disk
|
|
110
|
+
* @returns {Object} - Metrics object
|
|
111
|
+
*/
|
|
112
|
+
export function loadMetrics() {
|
|
113
|
+
try {
|
|
114
|
+
if (!fs.existsSync(METRICS_FILE)) {
|
|
115
|
+
const initial = createInitialMetrics();
|
|
116
|
+
saveMetrics(initial);
|
|
117
|
+
return initial;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const content = fs.readFileSync(METRICS_FILE, 'utf8');
|
|
121
|
+
return JSON.parse(content);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('Error loading metrics:', error.message);
|
|
124
|
+
return createInitialMetrics();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Save metrics to disk
|
|
130
|
+
* @param {Object} metrics - Metrics object
|
|
131
|
+
*/
|
|
132
|
+
export function saveMetrics(metrics) {
|
|
133
|
+
try {
|
|
134
|
+
if (!fs.existsSync(METRICS_DIR)) {
|
|
135
|
+
fs.mkdirSync(METRICS_DIR, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
metrics.updatedAt = new Date().toISOString();
|
|
139
|
+
fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2), 'utf8');
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('Error saving metrics:', error.message);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Append to metrics history
|
|
147
|
+
* @param {Object} snapshot - Current KPI snapshot
|
|
148
|
+
*/
|
|
149
|
+
function appendHistory(snapshot) {
|
|
150
|
+
try {
|
|
151
|
+
let history = [];
|
|
152
|
+
|
|
153
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
154
|
+
history = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
history.push({
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
...snapshot
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Keep only recent entries
|
|
163
|
+
if (history.length > MAX_HISTORY_ENTRIES) {
|
|
164
|
+
history = history.slice(-MAX_HISTORY_ENTRIES);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2), 'utf8');
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Error appending history:', error.message);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// KPI CALCULATION
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Recalculate all KPIs from counters
|
|
179
|
+
* @param {Object} metrics - Metrics object
|
|
180
|
+
* @returns {Object} - Updated metrics with KPIs
|
|
181
|
+
*/
|
|
182
|
+
export function recalculateKPIs(metrics) {
|
|
183
|
+
const c = metrics.counters;
|
|
184
|
+
const a = metrics.aggregates;
|
|
185
|
+
const kpis = {};
|
|
186
|
+
|
|
187
|
+
// Core Metrics (1-5)
|
|
188
|
+
kpis.task_success_rate = c.total_tasks > 0
|
|
189
|
+
? (c.successful_tasks / c.total_tasks * 100).toFixed(2)
|
|
190
|
+
: 0;
|
|
191
|
+
|
|
192
|
+
kpis.error_repeat_rate = c.total_errors > 0
|
|
193
|
+
? (c.repeated_errors / c.total_errors * 100).toFixed(2)
|
|
194
|
+
: 0;
|
|
195
|
+
|
|
196
|
+
kpis.first_time_success = c.total_tasks > 0
|
|
197
|
+
? (c.no_retry_tasks / c.total_tasks * 100).toFixed(2)
|
|
198
|
+
: 0;
|
|
199
|
+
|
|
200
|
+
kpis.time_to_completion = c.total_tasks > 0
|
|
201
|
+
? (a.total_task_duration_seconds / c.total_tasks).toFixed(2)
|
|
202
|
+
: 0;
|
|
203
|
+
|
|
204
|
+
kpis.human_intervention_rate = c.total_tasks > 0
|
|
205
|
+
? (c.manual_fixes / c.total_tasks * 100).toFixed(2)
|
|
206
|
+
: 0;
|
|
207
|
+
|
|
208
|
+
// Learning Metrics (6-10)
|
|
209
|
+
const totalDetections = c.true_positives + c.false_positives;
|
|
210
|
+
kpis.pattern_precision = totalDetections > 0
|
|
211
|
+
? (c.true_positives / totalDetections * 100).toFixed(2)
|
|
212
|
+
: 0;
|
|
213
|
+
|
|
214
|
+
const totalActual = c.true_positives + c.false_negatives;
|
|
215
|
+
kpis.pattern_recall = totalActual > 0
|
|
216
|
+
? (c.true_positives / totalActual * 100).toFixed(2)
|
|
217
|
+
: 0;
|
|
218
|
+
|
|
219
|
+
kpis.skill_effectiveness = c.tasks_where_skill_applied > 0
|
|
220
|
+
? (c.tasks_helped_by_skill / c.tasks_where_skill_applied * 100).toFixed(2)
|
|
221
|
+
: 0;
|
|
222
|
+
|
|
223
|
+
kpis.skill_coverage = c.total_tasks > 0
|
|
224
|
+
? (c.tasks_with_skill / c.total_tasks * 100).toFixed(2)
|
|
225
|
+
: 0;
|
|
226
|
+
|
|
227
|
+
kpis.false_positive_rate = c.total_alerts > 0
|
|
228
|
+
? (c.false_positives / c.total_alerts * 100).toFixed(2)
|
|
229
|
+
: 0;
|
|
230
|
+
|
|
231
|
+
// Evolution Metrics (11-15)
|
|
232
|
+
kpis.skills_auto_generated = c.auto_skills_created;
|
|
233
|
+
kpis.skills_pruned = c.skills_pruned;
|
|
234
|
+
|
|
235
|
+
kpis.pattern_confidence_avg = a.pattern_count > 0
|
|
236
|
+
? (a.pattern_confidence_sum / a.pattern_count).toFixed(3)
|
|
237
|
+
: 0;
|
|
238
|
+
|
|
239
|
+
kpis.ab_test_win_rate = c.total_ab_tests > 0
|
|
240
|
+
? (c.ab_tests_with_winner / c.total_ab_tests * 100).toFixed(2)
|
|
241
|
+
: 0;
|
|
242
|
+
|
|
243
|
+
kpis.learning_velocity = c.new_patterns_this_week;
|
|
244
|
+
|
|
245
|
+
// Improvement Metrics (16-18)
|
|
246
|
+
const lastWeek = metrics.weekly.last_week_success_rate;
|
|
247
|
+
const thisWeek = parseFloat(kpis.task_success_rate);
|
|
248
|
+
kpis.week_over_week_improvement = lastWeek && lastWeek > 0
|
|
249
|
+
? ((thisWeek - lastWeek) / lastWeek * 100).toFixed(2)
|
|
250
|
+
: 0;
|
|
251
|
+
|
|
252
|
+
kpis.error_reduction_rate = c.baseline_errors > 0
|
|
253
|
+
? ((1 - c.current_errors / c.baseline_errors) * 100).toFixed(2)
|
|
254
|
+
: 0;
|
|
255
|
+
|
|
256
|
+
kpis.skill_roi = c.total_skill_creation_time_seconds > 0
|
|
257
|
+
? (c.total_time_saved_seconds / c.total_skill_creation_time_seconds).toFixed(2)
|
|
258
|
+
: 0;
|
|
259
|
+
|
|
260
|
+
metrics.kpis = kpis;
|
|
261
|
+
return metrics;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ============================================================================
|
|
265
|
+
// EVENT RECORDING
|
|
266
|
+
// ============================================================================
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Record a task event
|
|
270
|
+
* @param {Object} event - Task event details
|
|
271
|
+
*/
|
|
272
|
+
export function recordTaskEvent(event) {
|
|
273
|
+
const metrics = loadMetrics();
|
|
274
|
+
const c = metrics.counters;
|
|
275
|
+
const a = metrics.aggregates;
|
|
276
|
+
|
|
277
|
+
c.total_tasks++;
|
|
278
|
+
|
|
279
|
+
if (event.success) {
|
|
280
|
+
c.successful_tasks++;
|
|
281
|
+
} else {
|
|
282
|
+
c.failed_tasks++;
|
|
283
|
+
c.total_errors++;
|
|
284
|
+
|
|
285
|
+
// Check if this error is a repeat
|
|
286
|
+
if (event.errorId && event.isRepeat) {
|
|
287
|
+
c.repeated_errors++;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (event.firstAttempt) {
|
|
292
|
+
c.no_retry_tasks++;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (event.manualFix) {
|
|
296
|
+
c.manual_fixes++;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (event.duration) {
|
|
300
|
+
a.total_task_duration_seconds += event.duration;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (event.skillApplied) {
|
|
304
|
+
c.tasks_with_skill++;
|
|
305
|
+
c.tasks_where_skill_applied++;
|
|
306
|
+
|
|
307
|
+
if (event.skillHelped) {
|
|
308
|
+
c.tasks_helped_by_skill++;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
recalculateKPIs(metrics);
|
|
313
|
+
saveMetrics(metrics);
|
|
314
|
+
|
|
315
|
+
return metrics;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Record a pattern event
|
|
320
|
+
* @param {Object} event - Pattern event details
|
|
321
|
+
*/
|
|
322
|
+
export function recordPatternEvent(event) {
|
|
323
|
+
const metrics = loadMetrics();
|
|
324
|
+
const c = metrics.counters;
|
|
325
|
+
const a = metrics.aggregates;
|
|
326
|
+
|
|
327
|
+
c.total_alerts++;
|
|
328
|
+
|
|
329
|
+
if (event.type === 'true_positive') {
|
|
330
|
+
c.true_positives++;
|
|
331
|
+
} else if (event.type === 'false_positive') {
|
|
332
|
+
c.false_positives++;
|
|
333
|
+
} else if (event.type === 'false_negative') {
|
|
334
|
+
c.false_negatives++;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (event.confidence !== undefined) {
|
|
338
|
+
a.pattern_confidence_sum += event.confidence;
|
|
339
|
+
a.pattern_count++;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (event.newPattern) {
|
|
343
|
+
c.new_patterns_this_week++;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
recalculateKPIs(metrics);
|
|
347
|
+
saveMetrics(metrics);
|
|
348
|
+
|
|
349
|
+
return metrics;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Record a skill event
|
|
354
|
+
* @param {Object} event - Skill event details
|
|
355
|
+
*/
|
|
356
|
+
export function recordSkillEvent(event) {
|
|
357
|
+
const metrics = loadMetrics();
|
|
358
|
+
const c = metrics.counters;
|
|
359
|
+
|
|
360
|
+
if (event.type === 'created') {
|
|
361
|
+
c.auto_skills_created++;
|
|
362
|
+
if (event.creationTime) {
|
|
363
|
+
c.total_skill_creation_time_seconds += event.creationTime;
|
|
364
|
+
}
|
|
365
|
+
} else if (event.type === 'pruned') {
|
|
366
|
+
c.skills_pruned++;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (event.timeSaved) {
|
|
370
|
+
c.total_time_saved_seconds += event.timeSaved;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
recalculateKPIs(metrics);
|
|
374
|
+
saveMetrics(metrics);
|
|
375
|
+
|
|
376
|
+
return metrics;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Record an A/B test result
|
|
381
|
+
* @param {Object} event - A/B test event details
|
|
382
|
+
*/
|
|
383
|
+
export function recordABTestEvent(event) {
|
|
384
|
+
const metrics = loadMetrics();
|
|
385
|
+
const c = metrics.counters;
|
|
386
|
+
|
|
387
|
+
c.total_ab_tests++;
|
|
388
|
+
|
|
389
|
+
if (event.hasWinner) {
|
|
390
|
+
c.ab_tests_with_winner++;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
recalculateKPIs(metrics);
|
|
394
|
+
saveMetrics(metrics);
|
|
395
|
+
|
|
396
|
+
return metrics;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Set baseline for error reduction tracking
|
|
401
|
+
*/
|
|
402
|
+
export function setErrorBaseline() {
|
|
403
|
+
const metrics = loadMetrics();
|
|
404
|
+
metrics.counters.baseline_errors = metrics.counters.total_errors;
|
|
405
|
+
metrics.counters.current_errors = 0;
|
|
406
|
+
saveMetrics(metrics);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Rotate weekly metrics (call at week start)
|
|
411
|
+
*/
|
|
412
|
+
export function rotateWeeklyMetrics() {
|
|
413
|
+
const metrics = loadMetrics();
|
|
414
|
+
const weekStart = getWeekStart();
|
|
415
|
+
|
|
416
|
+
// Save last week's success rate
|
|
417
|
+
metrics.weekly.last_week_success_rate = parseFloat(metrics.kpis.task_success_rate) || null;
|
|
418
|
+
|
|
419
|
+
// Reset weekly counters
|
|
420
|
+
metrics.counters.new_patterns_this_week = 0;
|
|
421
|
+
metrics.weekly.current_week_start = weekStart.toISOString();
|
|
422
|
+
|
|
423
|
+
recalculateKPIs(metrics);
|
|
424
|
+
saveMetrics(metrics);
|
|
425
|
+
|
|
426
|
+
// Take snapshot for history
|
|
427
|
+
appendHistory(metrics.kpis);
|
|
428
|
+
|
|
429
|
+
return metrics;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ============================================================================
|
|
433
|
+
// DASHBOARD API
|
|
434
|
+
// ============================================================================
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Get all current KPIs for Dashboard
|
|
438
|
+
* @returns {Object} - KPIs with validation status
|
|
439
|
+
*/
|
|
440
|
+
export function getDashboardData() {
|
|
441
|
+
const metrics = loadMetrics();
|
|
442
|
+
const dashboard = {
|
|
443
|
+
updatedAt: metrics.updatedAt,
|
|
444
|
+
kpis: {}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
Object.entries(metrics.kpis).forEach(([id, value]) => {
|
|
448
|
+
const validation = validateMetric(id, parseFloat(value));
|
|
449
|
+
const schema = Object.values(METRICS_SCHEMA).find(m => m.id === id);
|
|
450
|
+
|
|
451
|
+
dashboard.kpis[id] = {
|
|
452
|
+
value: parseFloat(value),
|
|
453
|
+
status: validation.status,
|
|
454
|
+
message: validation.message,
|
|
455
|
+
widget: schema?.dashboard?.widget || 'number',
|
|
456
|
+
color: schema?.dashboard?.color || 'gray',
|
|
457
|
+
name: schema?.name || id,
|
|
458
|
+
unit: schema?.unit || ''
|
|
459
|
+
};
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
return dashboard;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get metrics history for trend charts
|
|
467
|
+
* @param {string} metricId - Metric ID to get history for
|
|
468
|
+
* @param {number} limit - Number of entries to return
|
|
469
|
+
* @returns {Array} - Array of { timestamp, value } objects
|
|
470
|
+
*/
|
|
471
|
+
export function getMetricHistory(metricId, limit = 30) {
|
|
472
|
+
try {
|
|
473
|
+
if (!fs.existsSync(HISTORY_FILE)) {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const history = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
478
|
+
|
|
479
|
+
return history
|
|
480
|
+
.slice(-limit)
|
|
481
|
+
.map(entry => ({
|
|
482
|
+
timestamp: entry.timestamp,
|
|
483
|
+
value: entry[metricId] || 0
|
|
484
|
+
}));
|
|
485
|
+
} catch (error) {
|
|
486
|
+
return [];
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Get summary statistics
|
|
492
|
+
* @returns {Object} - Summary object
|
|
493
|
+
*/
|
|
494
|
+
export function getSummary() {
|
|
495
|
+
const metrics = loadMetrics();
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
totalTasks: metrics.counters.total_tasks,
|
|
499
|
+
successRate: metrics.kpis.task_success_rate,
|
|
500
|
+
skillsGenerated: metrics.counters.auto_skills_created,
|
|
501
|
+
patternsLearned: metrics.aggregates.pattern_count,
|
|
502
|
+
lastUpdated: metrics.updatedAt
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ============================================================================
|
|
507
|
+
// EXPORTS
|
|
508
|
+
// ============================================================================
|
|
509
|
+
|
|
510
|
+
export default {
|
|
511
|
+
loadMetrics,
|
|
512
|
+
saveMetrics,
|
|
513
|
+
recalculateKPIs,
|
|
514
|
+
recordTaskEvent,
|
|
515
|
+
recordPatternEvent,
|
|
516
|
+
recordSkillEvent,
|
|
517
|
+
recordABTestEvent,
|
|
518
|
+
setErrorBaseline,
|
|
519
|
+
rotateWeeklyMetrics,
|
|
520
|
+
getDashboardData,
|
|
521
|
+
getMetricHistory,
|
|
522
|
+
getSummary
|
|
523
|
+
};
|