@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,192 @@
1
+ "use strict";
2
+ /**
3
+ * Monitor Service - Ongoing model monitoring and alerting
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var __metadata = (this && this.__metadata) || function (k, v) {
12
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.MonitorService = void 0;
19
+ const core_1 = require("@hazeljs/core");
20
+ const core_2 = __importDefault(require("@hazeljs/core"));
21
+ const drift_service_1 = require("./drift.service");
22
+ let MonitorService = class MonitorService {
23
+ constructor(driftService) {
24
+ this.monitors = new Map();
25
+ this.alertHandlers = [];
26
+ this.checkIntervals = new Map();
27
+ this.accuracyHistory = new Map();
28
+ this.driftService = driftService;
29
+ }
30
+ /**
31
+ * Register a model for monitoring
32
+ */
33
+ registerModel(config) {
34
+ const key = this.getMonitorKey(config.modelName, config.modelVersion);
35
+ this.monitors.set(key, config);
36
+ // Set up periodic checks if interval specified
37
+ if (config.checkIntervalMinutes && config.checkIntervalMinutes > 0) {
38
+ const intervalMs = config.checkIntervalMinutes * 60 * 1000;
39
+ const interval = setInterval(() => {
40
+ this.checkModel(config.modelName, config.modelVersion);
41
+ }, intervalMs);
42
+ // Clean up old interval if exists
43
+ const oldInterval = this.checkIntervals.get(key);
44
+ if (oldInterval) {
45
+ clearInterval(oldInterval);
46
+ }
47
+ this.checkIntervals.set(key, interval);
48
+ }
49
+ core_2.default.debug(`Registered monitor for ${config.modelName}@${config.modelVersion ?? 'latest'}`);
50
+ }
51
+ /**
52
+ * Unregister a model from monitoring
53
+ */
54
+ unregisterModel(modelName, modelVersion) {
55
+ const key = this.getMonitorKey(modelName, modelVersion);
56
+ this.monitors.delete(key);
57
+ const interval = this.checkIntervals.get(key);
58
+ if (interval) {
59
+ clearInterval(interval);
60
+ this.checkIntervals.delete(key);
61
+ }
62
+ core_2.default.debug(`Unregistered monitor for ${modelName}@${modelVersion ?? 'latest'}`);
63
+ }
64
+ /**
65
+ * Add an alert handler
66
+ */
67
+ onAlert(handler) {
68
+ this.alertHandlers.push(handler);
69
+ }
70
+ /**
71
+ * Remove an alert handler
72
+ */
73
+ offAlert(handler) {
74
+ const idx = this.alertHandlers.indexOf(handler);
75
+ if (idx >= 0) {
76
+ this.alertHandlers.splice(idx, 1);
77
+ }
78
+ }
79
+ /**
80
+ * Record prediction for drift monitoring
81
+ */
82
+ recordPrediction(modelName, features, prediction) {
83
+ // This would store predictions for batch drift detection
84
+ // In a real implementation, this would write to a time-series DB
85
+ core_2.default.debug(`Recorded prediction for ${modelName}`, { features, prediction });
86
+ }
87
+ /**
88
+ * Record accuracy metric for accuracy monitoring
89
+ */
90
+ recordAccuracy(modelName, accuracy, modelVersion) {
91
+ const key = this.getMonitorKey(modelName, modelVersion);
92
+ const history = this.accuracyHistory.get(key) ?? [];
93
+ history.push({ timestamp: new Date(), accuracy });
94
+ this.accuracyHistory.set(key, history);
95
+ // Check if accuracy dropped below threshold
96
+ const config = this.monitors.get(key);
97
+ if (config?.accuracyMonitor) {
98
+ const { threshold, windowSize } = config.accuracyMonitor;
99
+ const recent = history.slice(-windowSize);
100
+ const avgAccuracy = recent.reduce((sum, h) => sum + h.accuracy, 0) / recent.length;
101
+ if (avgAccuracy < threshold) {
102
+ this.emitAlert({
103
+ timestamp: new Date(),
104
+ modelName,
105
+ modelVersion,
106
+ alertType: 'accuracy',
107
+ severity: 'critical',
108
+ message: `Average accuracy ${avgAccuracy.toFixed(4)} below threshold ${threshold} over last ${windowSize} checks`,
109
+ details: { avgAccuracy, threshold, windowSize, recentHistory: recent },
110
+ });
111
+ }
112
+ }
113
+ }
114
+ /**
115
+ * Check a model for drift and other issues
116
+ */
117
+ async checkModel(modelName, modelVersion) {
118
+ const key = this.getMonitorKey(modelName, modelVersion);
119
+ const config = this.monitors.get(key);
120
+ if (!config) {
121
+ throw new Error(`No monitor registered for ${modelName}@${modelVersion ?? 'latest'}`);
122
+ }
123
+ const results = [];
124
+ // Note: In a real implementation, this would fetch recent feature data
125
+ // from the feature store or prediction logs
126
+ // For now, this is a placeholder for the check structure
127
+ if (config.featureDrift) {
128
+ // This would be populated from actual feature data
129
+ const dummyFeatures = {};
130
+ try {
131
+ const report = this.driftService.detectDriftReport(dummyFeatures, config.featureDrift);
132
+ results.push(...report.results);
133
+ for (const result of report.results) {
134
+ if (result.driftDetected) {
135
+ this.emitAlert({
136
+ timestamp: new Date(),
137
+ modelName,
138
+ modelVersion,
139
+ alertType: 'drift',
140
+ severity: 'warning',
141
+ message: result.message,
142
+ details: { feature: result.feature, score: result.score, method: result.method },
143
+ });
144
+ }
145
+ }
146
+ }
147
+ catch (error) {
148
+ core_2.default.warn(`Failed to check drift for ${modelName}:`, error);
149
+ }
150
+ }
151
+ return results;
152
+ }
153
+ /**
154
+ * Get monitoring status for all registered models
155
+ */
156
+ getStatus() {
157
+ return Array.from(this.monitors.values()).map((config) => ({
158
+ modelName: config.modelName,
159
+ modelVersion: config.modelVersion,
160
+ isActive: this.checkIntervals.has(this.getMonitorKey(config.modelName, config.modelVersion)),
161
+ checkInterval: config.checkIntervalMinutes,
162
+ }));
163
+ }
164
+ /**
165
+ * Stop all monitoring
166
+ */
167
+ stop() {
168
+ for (const [key, interval] of this.checkIntervals) {
169
+ clearInterval(interval);
170
+ this.checkIntervals.delete(key);
171
+ }
172
+ core_2.default.debug('Stopped all monitoring');
173
+ }
174
+ getMonitorKey(modelName, modelVersion) {
175
+ return modelVersion ? `${modelName}:${modelVersion}` : modelName;
176
+ }
177
+ async emitAlert(alert) {
178
+ for (const handler of this.alertHandlers) {
179
+ try {
180
+ await handler(alert);
181
+ }
182
+ catch (error) {
183
+ core_2.default.error('Alert handler failed:', error);
184
+ }
185
+ }
186
+ }
187
+ };
188
+ exports.MonitorService = MonitorService;
189
+ exports.MonitorService = MonitorService = __decorate([
190
+ (0, core_1.Service)(),
191
+ __metadata("design:paramtypes", [drift_service_1.DriftService])
192
+ ], MonitorService);
@@ -96,4 +96,109 @@ describe('TrainerService', () => {
96
96
  });
97
97
  await expect(trainer.train('broken-train', {})).rejects.toThrow('Training method train not found on model');
98
98
  });
99
+ it('should train with specific version', async () => {
100
+ let VersionedModel = class VersionedModel {
101
+ async train(data) {
102
+ return { version: '2.0.0', data };
103
+ }
104
+ predict() { }
105
+ };
106
+ __decorate([
107
+ (0, decorators_1.Train)(),
108
+ __metadata("design:type", Function),
109
+ __metadata("design:paramtypes", [Object]),
110
+ __metadata("design:returntype", Promise)
111
+ ], VersionedModel.prototype, "train", null);
112
+ __decorate([
113
+ (0, decorators_1.Predict)(),
114
+ __metadata("design:type", Function),
115
+ __metadata("design:paramtypes", []),
116
+ __metadata("design:returntype", void 0)
117
+ ], VersionedModel.prototype, "predict", null);
118
+ VersionedModel = __decorate([
119
+ (0, decorators_1.Model)({ name: 'versioned-model', version: '2.0.0', framework: 'custom' })
120
+ ], VersionedModel);
121
+ const instance = new VersionedModel();
122
+ registry.register({
123
+ metadata: { name: 'versioned-model', version: '2.0.0', framework: 'custom' },
124
+ instance,
125
+ trainMethod: 'train',
126
+ predictMethod: 'predict',
127
+ });
128
+ const result = await trainer.train('versioned-model', { test: true }, '2.0.0');
129
+ expect(result.version).toBe('2.0.0');
130
+ });
131
+ it('should handle training errors gracefully', async () => {
132
+ let ErrorModel = class ErrorModel {
133
+ async train() {
134
+ throw new Error('Training failed');
135
+ }
136
+ predict() { }
137
+ };
138
+ __decorate([
139
+ (0, decorators_1.Train)(),
140
+ __metadata("design:type", Function),
141
+ __metadata("design:paramtypes", []),
142
+ __metadata("design:returntype", Promise)
143
+ ], ErrorModel.prototype, "train", null);
144
+ __decorate([
145
+ (0, decorators_1.Predict)(),
146
+ __metadata("design:type", Function),
147
+ __metadata("design:paramtypes", []),
148
+ __metadata("design:returntype", void 0)
149
+ ], ErrorModel.prototype, "predict", null);
150
+ ErrorModel = __decorate([
151
+ (0, decorators_1.Model)({ name: 'error-model', version: '1.0.0', framework: 'custom' })
152
+ ], ErrorModel);
153
+ const instance = new ErrorModel();
154
+ registry.register({
155
+ metadata: { name: 'error-model', version: '1.0.0', framework: 'custom' },
156
+ instance,
157
+ trainMethod: 'train',
158
+ predictMethod: 'predict',
159
+ });
160
+ await expect(trainer.train('error-model', {})).rejects.toThrow('Training failed');
161
+ });
162
+ it('should discover train method from decorated class', () => {
163
+ let DiscoverModel = class DiscoverModel {
164
+ customTrainMethod() { }
165
+ predict() { }
166
+ };
167
+ __decorate([
168
+ (0, decorators_1.Train)(),
169
+ __metadata("design:type", Function),
170
+ __metadata("design:paramtypes", []),
171
+ __metadata("design:returntype", void 0)
172
+ ], DiscoverModel.prototype, "customTrainMethod", null);
173
+ __decorate([
174
+ (0, decorators_1.Predict)(),
175
+ __metadata("design:type", Function),
176
+ __metadata("design:paramtypes", []),
177
+ __metadata("design:returntype", void 0)
178
+ ], DiscoverModel.prototype, "predict", null);
179
+ DiscoverModel = __decorate([
180
+ (0, decorators_1.Model)({ name: 'discover-model', version: '1.0.0', framework: 'custom' })
181
+ ], DiscoverModel);
182
+ const instance = new DiscoverModel();
183
+ const method = trainer.discoverTrainMethod(instance);
184
+ expect(method).toBe('customTrainMethod');
185
+ });
186
+ it('should return undefined when no train method decorated', () => {
187
+ let NoDecoratorModel = class NoDecoratorModel {
188
+ train() { }
189
+ predict() { }
190
+ };
191
+ __decorate([
192
+ (0, decorators_1.Predict)(),
193
+ __metadata("design:type", Function),
194
+ __metadata("design:paramtypes", []),
195
+ __metadata("design:returntype", void 0)
196
+ ], NoDecoratorModel.prototype, "predict", null);
197
+ NoDecoratorModel = __decorate([
198
+ (0, decorators_1.Model)({ name: 'no-decorator', version: '1.0.0', framework: 'custom' })
199
+ ], NoDecoratorModel);
200
+ const instance = new NoDecoratorModel();
201
+ const method = trainer.discoverTrainMethod(instance);
202
+ expect(method).toBeUndefined();
203
+ });
99
204
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/ml",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Machine Learning & Model Management for HazelJS framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -48,5 +48,5 @@
48
48
  "url": "https://github.com/hazeljs/hazel-js/issues"
49
49
  },
50
50
  "homepage": "https://hazeljs.ai",
51
- "gitHead": "e0c2d21ace726f2a0bf20c5baefad02e1e681ba9"
51
+ "gitHead": "14c91213d93aa7eb907cb71b1d7104ddc4556327"
52
52
  }