@hazeljs/ml 0.2.4 → 0.3.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 (69) hide show
  1. package/dist/evaluation/metrics.service.test.js +288 -0
  2. package/dist/experiments/__tests__/experiment.decorator.test.d.ts +2 -0
  3. package/dist/experiments/__tests__/experiment.decorator.test.d.ts.map +1 -0
  4. package/dist/experiments/__tests__/experiment.decorator.test.js +121 -0
  5. package/dist/experiments/__tests__/experiment.service.test.d.ts +2 -0
  6. package/dist/experiments/__tests__/experiment.service.test.d.ts.map +1 -0
  7. package/dist/experiments/__tests__/experiment.service.test.js +460 -0
  8. package/dist/experiments/experiment.decorator.d.ts +44 -0
  9. package/dist/experiments/experiment.decorator.d.ts.map +1 -0
  10. package/dist/experiments/experiment.decorator.js +51 -0
  11. package/dist/experiments/experiment.service.d.ts +42 -0
  12. package/dist/experiments/experiment.service.d.ts.map +1 -0
  13. package/dist/experiments/experiment.service.js +355 -0
  14. package/dist/experiments/experiment.types.d.ts +60 -0
  15. package/dist/experiments/experiment.types.d.ts.map +1 -0
  16. package/dist/experiments/experiment.types.js +5 -0
  17. package/dist/experiments/index.d.ts +9 -0
  18. package/dist/experiments/index.d.ts.map +1 -0
  19. package/dist/experiments/index.js +16 -0
  20. package/dist/features/__tests__/feature-view.decorator.test.d.ts +2 -0
  21. package/dist/features/__tests__/feature-view.decorator.test.d.ts.map +1 -0
  22. package/dist/features/__tests__/feature-view.decorator.test.js +168 -0
  23. package/dist/features/__tests__/feature.decorator.test.d.ts +2 -0
  24. package/dist/features/__tests__/feature.decorator.test.d.ts.map +1 -0
  25. package/dist/features/__tests__/feature.decorator.test.js +167 -0
  26. package/dist/features/feature-store.service.d.ts +59 -0
  27. package/dist/features/feature-store.service.d.ts.map +1 -0
  28. package/dist/features/feature-store.service.js +197 -0
  29. package/dist/features/feature-view.decorator.d.ts +52 -0
  30. package/dist/features/feature-view.decorator.d.ts.map +1 -0
  31. package/dist/features/feature-view.decorator.js +54 -0
  32. package/dist/features/feature.decorator.d.ts +42 -0
  33. package/dist/features/feature.decorator.d.ts.map +1 -0
  34. package/dist/features/feature.decorator.js +49 -0
  35. package/dist/features/feature.types.d.ts +93 -0
  36. package/dist/features/feature.types.d.ts.map +1 -0
  37. package/dist/features/feature.types.js +5 -0
  38. package/dist/features/index.d.ts +12 -0
  39. package/dist/features/index.d.ts.map +1 -0
  40. package/dist/features/index.js +29 -0
  41. package/dist/features/offline-store.d.ts +40 -0
  42. package/dist/features/offline-store.d.ts.map +1 -0
  43. package/dist/features/offline-store.js +215 -0
  44. package/dist/features/online-store.d.ts +45 -0
  45. package/dist/features/online-store.d.ts.map +1 -0
  46. package/dist/features/online-store.js +139 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +26 -1
  50. package/dist/monitoring/__tests__/drift.service.test.d.ts +2 -0
  51. package/dist/monitoring/__tests__/drift.service.test.d.ts.map +1 -0
  52. package/dist/monitoring/__tests__/drift.service.test.js +362 -0
  53. package/dist/monitoring/__tests__/monitor.service.test.d.ts +2 -0
  54. package/dist/monitoring/__tests__/monitor.service.test.d.ts.map +1 -0
  55. package/dist/monitoring/__tests__/monitor.service.test.js +360 -0
  56. package/dist/monitoring/drift.service.d.ts +68 -0
  57. package/dist/monitoring/drift.service.d.ts.map +1 -0
  58. package/dist/monitoring/drift.service.js +360 -0
  59. package/dist/monitoring/drift.types.d.ts +44 -0
  60. package/dist/monitoring/drift.types.d.ts.map +1 -0
  61. package/dist/monitoring/drift.types.js +5 -0
  62. package/dist/monitoring/index.d.ts +10 -0
  63. package/dist/monitoring/index.d.ts.map +1 -0
  64. package/dist/monitoring/index.js +13 -0
  65. package/dist/monitoring/monitor.service.d.ts +79 -0
  66. package/dist/monitoring/monitor.service.d.ts.map +1 -0
  67. package/dist/monitoring/monitor.service.js +192 -0
  68. package/dist/training/trainer.service.test.js +105 -0
  69. package/package.json +2 -2
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=drift.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.service.test.d.ts","sourceRoot":"","sources":["../../../src/monitoring/__tests__/drift.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,362 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const drift_service_1 = require("../drift.service");
4
+ describe('DriftService', () => {
5
+ let service;
6
+ beforeEach(() => {
7
+ service = new drift_service_1.DriftService();
8
+ });
9
+ describe('setReferenceDistribution', () => {
10
+ it('should store reference distribution', () => {
11
+ const values = [1, 2, 3, 4, 5];
12
+ service.setReferenceDistribution('feature1', values);
13
+ // Verify by detecting drift (which requires reference)
14
+ const result = service.detectDrift('feature1', [1, 2, 3, 4, 5], {
15
+ method: 'psi',
16
+ threshold: 0.25,
17
+ });
18
+ expect(result).toBeDefined();
19
+ });
20
+ it('should copy values array', () => {
21
+ const values = [1, 2, 3];
22
+ service.setReferenceDistribution('feature1', values);
23
+ values.push(4); // Modify original
24
+ const result = service.detectDrift('feature1', [1, 2, 3], {
25
+ method: 'psi',
26
+ threshold: 0.25,
27
+ });
28
+ expect(result.score).toBeLessThan(0.1); // Should match original 3 values
29
+ });
30
+ });
31
+ describe('calculateStats', () => {
32
+ it('should calculate basic statistics', () => {
33
+ const values = [1, 2, 3, 4, 5];
34
+ const stats = service.calculateStats(values);
35
+ expect(stats.count).toBe(5);
36
+ expect(stats.min).toBe(1);
37
+ expect(stats.max).toBe(5);
38
+ expect(stats.mean).toBe(3);
39
+ expect(stats.median).toBe(3);
40
+ });
41
+ it('should calculate median for even count', () => {
42
+ const values = [1, 2, 3, 4];
43
+ const stats = service.calculateStats(values);
44
+ expect(stats.median).toBe(2.5);
45
+ });
46
+ it('should calculate median for odd count', () => {
47
+ const values = [1, 2, 3, 4, 5];
48
+ const stats = service.calculateStats(values);
49
+ expect(stats.median).toBe(3);
50
+ });
51
+ it('should calculate standard deviation', () => {
52
+ const values = [1, 2, 3, 4, 5];
53
+ const stats = service.calculateStats(values);
54
+ expect(stats.std).toBeGreaterThan(0);
55
+ expect(stats.std).toBeCloseTo(1.414, 2);
56
+ });
57
+ it('should calculate histogram', () => {
58
+ const values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
59
+ const stats = service.calculateStats(values);
60
+ expect(stats.histogram).toHaveLength(10);
61
+ expect(stats.histogram[0].bin).toBe(0);
62
+ });
63
+ it('should calculate percentiles', () => {
64
+ const values = Array.from({ length: 100 }, (_, i) => i + 1);
65
+ const stats = service.calculateStats(values);
66
+ expect(stats.percentiles.p5).toBeDefined();
67
+ expect(stats.percentiles.p25).toBeDefined();
68
+ expect(stats.percentiles.p50).toBeDefined();
69
+ expect(stats.percentiles.p75).toBeDefined();
70
+ expect(stats.percentiles.p95).toBeDefined();
71
+ });
72
+ it('should handle single value', () => {
73
+ const values = [5];
74
+ const stats = service.calculateStats(values);
75
+ expect(stats.count).toBe(1);
76
+ expect(stats.min).toBe(5);
77
+ expect(stats.max).toBe(5);
78
+ expect(stats.mean).toBe(5);
79
+ expect(stats.median).toBe(5);
80
+ expect(stats.std).toBe(0);
81
+ });
82
+ it('should handle identical values', () => {
83
+ const values = [5, 5, 5, 5, 5];
84
+ const stats = service.calculateStats(values);
85
+ expect(stats.mean).toBe(5);
86
+ expect(stats.std).toBe(0);
87
+ });
88
+ });
89
+ describe('calculatePSI', () => {
90
+ it('should return low PSI for identical distributions', () => {
91
+ const reference = Array.from({ length: 100 }, (_, i) => i);
92
+ const current = Array.from({ length: 100 }, (_, i) => i);
93
+ const psi = service.calculatePSI(reference, current);
94
+ expect(psi).toBeLessThan(0.1);
95
+ });
96
+ it('should calculate PSI for different distributions', () => {
97
+ const reference = Array.from({ length: 100 }, (_, i) => i);
98
+ const current = Array.from({ length: 100 }, (_, i) => i + 100);
99
+ const psi = service.calculatePSI(reference, current);
100
+ expect(psi).toBeGreaterThanOrEqual(0);
101
+ });
102
+ it('should handle custom bin count', () => {
103
+ const reference = Array.from({ length: 50 }, (_, i) => i);
104
+ const current = Array.from({ length: 50 }, (_, i) => i);
105
+ const psi = service.calculatePSI(reference, current, 5);
106
+ expect(psi).toBeLessThan(0.1);
107
+ });
108
+ it('should handle distributions with different ranges', () => {
109
+ const reference = Array.from({ length: 50 }, (_, i) => i);
110
+ const current = Array.from({ length: 50 }, (_, i) => i + 25);
111
+ const psi = service.calculatePSI(reference, current);
112
+ expect(psi).toBeGreaterThan(0);
113
+ });
114
+ });
115
+ describe('calculateKS', () => {
116
+ it('should return low KS for identical distributions', () => {
117
+ const reference = [1, 2, 3, 4, 5];
118
+ const current = [1, 2, 3, 4, 5];
119
+ const result = service.calculateKS(reference, current);
120
+ expect(result.d).toBeLessThan(0.1);
121
+ expect(result.pValue).toBeDefined();
122
+ });
123
+ it('should return higher KS for different distributions', () => {
124
+ const reference = [1, 2, 3, 4, 5];
125
+ const current = [10, 20, 30, 40, 50];
126
+ const result = service.calculateKS(reference, current);
127
+ expect(result.d).toBeGreaterThan(0.5);
128
+ });
129
+ });
130
+ describe('detectDrift', () => {
131
+ it('should detect no drift for similar distributions using PSI', () => {
132
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
133
+ const result = service.detectDrift('feature1', [1, 2, 3, 4, 5], {
134
+ method: 'psi',
135
+ threshold: 0.25,
136
+ });
137
+ expect(result.driftDetected).toBe(false);
138
+ expect(result.score).toBeLessThan(0.1);
139
+ expect(result.feature).toBe('feature1');
140
+ expect(result.method).toBe('psi');
141
+ });
142
+ it('should detect drift for different distributions', () => {
143
+ const reference = Array.from({ length: 100 }, (_, i) => i);
144
+ const current = Array.from({ length: 100 }, (_, i) => i + 100);
145
+ service.setReferenceDistribution('feature1', reference);
146
+ const result = service.detectDrift('feature1', current, {
147
+ method: 'psi',
148
+ threshold: 0.25,
149
+ });
150
+ expect(result.feature).toBe('feature1');
151
+ expect(result.method).toBe('psi');
152
+ expect(result.score).toBeGreaterThanOrEqual(0);
153
+ });
154
+ it('should throw error when reference not set', () => {
155
+ expect(() => {
156
+ service.detectDrift('unknown-feature', [1, 2, 3], {
157
+ method: 'psi',
158
+ threshold: 0.25,
159
+ });
160
+ }).toThrow('No reference distribution');
161
+ });
162
+ it('should support KS method', () => {
163
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
164
+ const result = service.detectDrift('feature1', [1, 2, 3, 4, 5], {
165
+ method: 'ks',
166
+ threshold: 0.3,
167
+ });
168
+ expect(result.method).toBe('ks');
169
+ expect(result.pValue).toBeDefined();
170
+ });
171
+ it('should support JSD method', () => {
172
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
173
+ const result = service.detectDrift('feature1', [1, 2, 3, 4, 5], {
174
+ method: 'jsd',
175
+ threshold: 0.1,
176
+ });
177
+ expect(result.method).toBe('jsd');
178
+ });
179
+ it('should support Wasserstein method', () => {
180
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
181
+ const result = service.detectDrift('feature1', [1, 2, 3, 4, 5], {
182
+ method: 'wasserstein',
183
+ threshold: 1.0,
184
+ });
185
+ expect(result.method).toBe('wasserstein');
186
+ });
187
+ });
188
+ describe('calculateJSD', () => {
189
+ it('should return low JSD for identical distributions', () => {
190
+ const reference = [1, 2, 3, 4, 5];
191
+ const current = [1, 2, 3, 4, 5];
192
+ const jsd = service.calculateJSD(reference, current);
193
+ expect(jsd).toBeLessThan(0.1);
194
+ });
195
+ it('should return higher JSD for different distributions', () => {
196
+ const reference = [1, 2, 3, 4, 5];
197
+ const current = [10, 20, 30, 40, 50];
198
+ const jsd = service.calculateJSD(reference, current);
199
+ expect(jsd).toBeGreaterThan(0);
200
+ });
201
+ });
202
+ describe('calculateWasserstein', () => {
203
+ it('should return low distance for identical distributions', () => {
204
+ const reference = [1, 2, 3, 4, 5];
205
+ const current = [1, 2, 3, 4, 5];
206
+ const distance = service.calculateWasserstein(reference, current);
207
+ expect(distance).toBe(0);
208
+ });
209
+ it('should return higher distance for different distributions', () => {
210
+ const reference = [1, 2, 3, 4, 5];
211
+ const current = [10, 20, 30, 40, 50];
212
+ const distance = service.calculateWasserstein(reference, current);
213
+ expect(distance).toBeGreaterThan(0);
214
+ });
215
+ it('should handle distributions of different lengths', () => {
216
+ const reference = [1, 2, 3, 4, 5];
217
+ const current = [1, 2, 3];
218
+ const distance = service.calculateWasserstein(reference, current);
219
+ expect(distance).toBeGreaterThanOrEqual(0);
220
+ });
221
+ });
222
+ describe('detectDrift with different methods', () => {
223
+ it('should throw error for unsupported method', () => {
224
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
225
+ expect(() => {
226
+ service.detectDrift('feature1', [1, 2, 3, 4, 5], {
227
+ method: 'unknown',
228
+ threshold: 0.25,
229
+ });
230
+ }).toThrow('Unsupported drift detection method');
231
+ });
232
+ it('should include message in result', () => {
233
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
234
+ const result = service.detectDrift('feature1', [1, 2, 3, 4, 5], {
235
+ method: 'psi',
236
+ threshold: 0.25,
237
+ });
238
+ expect(result.message).toBeDefined();
239
+ expect(result.timestamp).toBeInstanceOf(Date);
240
+ });
241
+ });
242
+ describe('calculateChiSquare', () => {
243
+ it('should calculate chi-square for categorical data', () => {
244
+ const reference = { a: 10, b: 20, c: 30 };
245
+ const current = { a: 15, b: 25, c: 20 };
246
+ const result = service.calculateChiSquare(reference, current);
247
+ expect(result.chi2).toBeGreaterThanOrEqual(0);
248
+ expect(result.pValue).toBeGreaterThanOrEqual(0);
249
+ expect(result.pValue).toBeLessThanOrEqual(1);
250
+ });
251
+ it('should handle new categories in current', () => {
252
+ const reference = { a: 10, b: 20 };
253
+ const current = { a: 10, b: 20, c: 10 };
254
+ const result = service.calculateChiSquare(reference, current);
255
+ expect(result.chi2).toBeGreaterThanOrEqual(0);
256
+ });
257
+ it('should handle missing categories in current', () => {
258
+ const reference = { a: 10, b: 20, c: 30 };
259
+ const current = { a: 10, b: 20 };
260
+ const result = service.calculateChiSquare(reference, current);
261
+ expect(result.chi2).toBeGreaterThanOrEqual(0);
262
+ });
263
+ });
264
+ describe('detectDriftReport', () => {
265
+ it('should generate drift report for multiple features', () => {
266
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
267
+ service.setReferenceDistribution('feature2', [10, 20, 30, 40, 50]);
268
+ const report = service.detectDriftReport({
269
+ feature1: [1, 2, 3, 4, 5],
270
+ feature2: [10, 20, 30, 40, 50],
271
+ }, {
272
+ method: 'psi',
273
+ threshold: 0.25,
274
+ windowSize: 100,
275
+ });
276
+ expect(report.timestamp).toBeInstanceOf(Date);
277
+ expect(report.totalFeatures).toBe(2);
278
+ expect(report.results).toHaveLength(2);
279
+ });
280
+ it('should calculate drift percentage', () => {
281
+ service.setReferenceDistribution('feature1', [1, 2, 3, 4, 5]);
282
+ const report = service.detectDriftReport({
283
+ feature1: [1, 2, 3, 4, 5],
284
+ }, {
285
+ method: 'psi',
286
+ threshold: 0.25,
287
+ windowSize: 100,
288
+ });
289
+ expect(report.driftPercentage).toBeGreaterThanOrEqual(0);
290
+ expect(report.driftPercentage).toBeLessThanOrEqual(100);
291
+ });
292
+ });
293
+ describe('detectPredictionDrift', () => {
294
+ it('should detect drift in numeric predictions', () => {
295
+ const referencePredictions = [1, 2, 3, 4, 5];
296
+ const currentPredictions = [1, 2, 3, 4, 5];
297
+ const result = service.detectPredictionDrift(referencePredictions, currentPredictions);
298
+ expect(result.feature).toBe('__prediction__');
299
+ expect(result.driftDetected).toBeDefined();
300
+ });
301
+ it('should detect drift in categorical predictions', () => {
302
+ const referencePredictions = ['a', 'b', 'c', 'a', 'b'];
303
+ const currentPredictions = ['a', 'b', 'c', 'a', 'b'];
304
+ const result = service.detectPredictionDrift(referencePredictions, currentPredictions);
305
+ expect(result.feature).toBe('prediction');
306
+ expect(result.driftDetected).toBeDefined();
307
+ });
308
+ });
309
+ describe('detectCategoricalDrift', () => {
310
+ it('should detect categorical drift using chi-square', () => {
311
+ service.setReferenceDistribution('category_feature', ['a', 'b', 'c', 'a', 'b']);
312
+ const result = service.detectCategoricalDrift('category_feature', ['a', 'b', 'c', 'a', 'b'], {
313
+ threshold: 0.05,
314
+ windowSize: 100,
315
+ });
316
+ expect(result.feature).toBe('category_feature');
317
+ expect(result.method).toBe('chi2');
318
+ expect(result.driftDetected).toBeDefined();
319
+ });
320
+ it('should throw error when reference not set', () => {
321
+ expect(() => {
322
+ service.detectCategoricalDrift('unknown', ['a', 'b'], {
323
+ threshold: 0.05,
324
+ windowSize: 100,
325
+ });
326
+ }).toThrow('No reference distribution');
327
+ });
328
+ });
329
+ describe('countCategories', () => {
330
+ it('should count category frequencies', () => {
331
+ const values = ['a', 'b', 'a', 'c', 'b', 'a'];
332
+ const counts = service.countCategories(values);
333
+ expect(counts.a).toBe(3);
334
+ expect(counts.b).toBe(2);
335
+ expect(counts.c).toBe(1);
336
+ });
337
+ it('should handle empty array', () => {
338
+ const counts = service.countCategories([]);
339
+ expect(Object.keys(counts)).toHaveLength(0);
340
+ });
341
+ });
342
+ describe('edge cases', () => {
343
+ it('should handle empty distributions in calculateStats', () => {
344
+ const stats = service.calculateStats([5]);
345
+ expect(stats.count).toBe(1);
346
+ expect(stats.min).toBe(5);
347
+ expect(stats.max).toBe(5);
348
+ });
349
+ it('should handle distributions with zero range', () => {
350
+ const values = [5, 5, 5, 5, 5];
351
+ const stats = service.calculateStats(values);
352
+ expect(stats.std).toBe(0);
353
+ expect(stats.histogram).toHaveLength(10);
354
+ });
355
+ it('should handle single bin in PSI calculation', () => {
356
+ const reference = [1, 2, 3];
357
+ const current = [1, 2, 3];
358
+ const psi = service.calculatePSI(reference, current, 1);
359
+ expect(psi).toBeGreaterThanOrEqual(0);
360
+ });
361
+ });
362
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=monitor.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.service.test.d.ts","sourceRoot":"","sources":["../../../src/monitoring/__tests__/monitor.service.test.ts"],"names":[],"mappings":""}