@hazeljs/ml 0.2.0-beta.54 → 0.2.0-beta.55

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 (34) hide show
  1. package/dist/decorators/model.decorator.test.d.ts +2 -0
  2. package/dist/decorators/model.decorator.test.d.ts.map +1 -0
  3. package/dist/decorators/model.decorator.test.js +128 -0
  4. package/dist/evaluation/metrics.service.js +1 -1
  5. package/dist/evaluation/metrics.service.test.d.ts +2 -0
  6. package/dist/evaluation/metrics.service.test.d.ts.map +1 -0
  7. package/dist/evaluation/metrics.service.test.js +79 -0
  8. package/dist/inference/batch.service.js +1 -1
  9. package/dist/inference/batch.service.test.d.ts +2 -0
  10. package/dist/inference/batch.service.test.d.ts.map +1 -0
  11. package/dist/inference/batch.service.test.js +61 -0
  12. package/dist/inference/predictor.service.js +1 -1
  13. package/dist/inference/predictor.service.test.d.ts +2 -0
  14. package/dist/inference/predictor.service.test.d.ts.map +1 -0
  15. package/dist/inference/predictor.service.test.js +86 -0
  16. package/dist/ml-model.base.test.d.ts +2 -0
  17. package/dist/ml-model.base.test.d.ts.map +1 -0
  18. package/dist/ml-model.base.test.js +57 -0
  19. package/dist/ml.module.test.d.ts +2 -0
  20. package/dist/ml.module.test.d.ts.map +1 -0
  21. package/dist/ml.module.test.js +60 -0
  22. package/dist/registry/model.registry.js +1 -1
  23. package/dist/registry/model.registry.test.d.ts +2 -0
  24. package/dist/registry/model.registry.test.d.ts.map +1 -0
  25. package/dist/registry/model.registry.test.js +76 -0
  26. package/dist/training/pipeline.service.js +1 -1
  27. package/dist/training/pipeline.service.test.d.ts +2 -0
  28. package/dist/training/pipeline.service.test.d.ts.map +1 -0
  29. package/dist/training/pipeline.service.test.js +38 -0
  30. package/dist/training/trainer.service.js +1 -1
  31. package/dist/training/trainer.service.test.d.ts +2 -0
  32. package/dist/training/trainer.service.test.d.ts.map +1 -0
  33. package/dist/training/trainer.service.test.js +89 -0
  34. package/package.json +2 -2
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=model.decorator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.decorator.test.d.ts","sourceRoot":"","sources":["../../src/decorators/model.decorator.test.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("reflect-metadata");
13
+ const model_decorator_1 = require("./model.decorator");
14
+ const train_decorator_1 = require("./train.decorator");
15
+ const predict_decorator_1 = require("./predict.decorator");
16
+ describe('Model decorator', () => {
17
+ it('applies metadata to class', () => {
18
+ let TestModel = class TestModel {
19
+ };
20
+ TestModel = __decorate([
21
+ (0, model_decorator_1.Model)({ name: 'test-model', version: '1.0.0', framework: 'tensorflow' })
22
+ ], TestModel);
23
+ const meta = (0, model_decorator_1.getModelMetadata)(TestModel);
24
+ expect(meta).toEqual({
25
+ name: 'test-model',
26
+ version: '1.0.0',
27
+ framework: 'tensorflow',
28
+ description: '',
29
+ tags: [],
30
+ });
31
+ expect((0, model_decorator_1.hasModelMetadata)(TestModel)).toBe(true);
32
+ });
33
+ it('merges optional fields', () => {
34
+ let CustomModel = class CustomModel {
35
+ };
36
+ CustomModel = __decorate([
37
+ (0, model_decorator_1.Model)({
38
+ name: 'custom',
39
+ version: '2.0.0',
40
+ framework: 'onnx',
41
+ description: 'A model',
42
+ tags: ['production'],
43
+ })
44
+ ], CustomModel);
45
+ const meta = (0, model_decorator_1.getModelMetadata)(CustomModel);
46
+ expect(meta?.description).toBe('A model');
47
+ expect(meta?.tags).toEqual(['production']);
48
+ });
49
+ it('hasModelMetadata returns false for undecorated class', () => {
50
+ class PlainClass {
51
+ }
52
+ expect((0, model_decorator_1.hasModelMetadata)(PlainClass)).toBe(false);
53
+ expect((0, model_decorator_1.getModelMetadata)(PlainClass)).toBeUndefined();
54
+ });
55
+ });
56
+ describe('Train decorator', () => {
57
+ it('applies metadata to method', () => {
58
+ class TestClass {
59
+ train() { }
60
+ }
61
+ __decorate([
62
+ (0, train_decorator_1.Train)({ pipeline: 'default', epochs: 5 }),
63
+ __metadata("design:type", Function),
64
+ __metadata("design:paramtypes", []),
65
+ __metadata("design:returntype", void 0)
66
+ ], TestClass.prototype, "train", null);
67
+ const meta = (0, train_decorator_1.getTrainMetadata)(TestClass.prototype, 'train');
68
+ expect(meta).toEqual({
69
+ pipeline: 'default',
70
+ batchSize: 32,
71
+ epochs: 5,
72
+ });
73
+ expect((0, train_decorator_1.hasTrainMetadata)(TestClass.prototype, 'train')).toBe(true);
74
+ });
75
+ it('uses defaults when no options', () => {
76
+ class TestClass {
77
+ train() { }
78
+ }
79
+ __decorate([
80
+ (0, train_decorator_1.Train)(),
81
+ __metadata("design:type", Function),
82
+ __metadata("design:paramtypes", []),
83
+ __metadata("design:returntype", void 0)
84
+ ], TestClass.prototype, "train", null);
85
+ const meta = (0, train_decorator_1.getTrainMetadata)(TestClass.prototype, 'train');
86
+ expect(meta?.batchSize).toBe(32);
87
+ expect(meta?.epochs).toBe(10);
88
+ });
89
+ it('hasTrainMetadata returns false for undecorated method', () => {
90
+ class TestClass {
91
+ train() { }
92
+ }
93
+ expect((0, train_decorator_1.hasTrainMetadata)(TestClass.prototype, 'train')).toBe(false);
94
+ });
95
+ });
96
+ describe('Predict decorator', () => {
97
+ it('applies metadata to method', () => {
98
+ class TestClass {
99
+ predict() { }
100
+ }
101
+ __decorate([
102
+ (0, predict_decorator_1.Predict)({ endpoint: '/predict', batch: true }),
103
+ __metadata("design:type", Function),
104
+ __metadata("design:paramtypes", []),
105
+ __metadata("design:returntype", void 0)
106
+ ], TestClass.prototype, "predict", null);
107
+ const meta = (0, predict_decorator_1.getPredictMetadata)(TestClass.prototype, 'predict');
108
+ expect(meta).toEqual({
109
+ batch: true,
110
+ endpoint: '/predict',
111
+ });
112
+ expect((0, predict_decorator_1.hasPredictMetadata)(TestClass.prototype, 'predict')).toBe(true);
113
+ });
114
+ it('uses defaults when no options', () => {
115
+ class TestClass {
116
+ predict() { }
117
+ }
118
+ __decorate([
119
+ (0, predict_decorator_1.Predict)(),
120
+ __metadata("design:type", Function),
121
+ __metadata("design:paramtypes", []),
122
+ __metadata("design:returntype", void 0)
123
+ ], TestClass.prototype, "predict", null);
124
+ const meta = (0, predict_decorator_1.getPredictMetadata)(TestClass.prototype, 'predict');
125
+ expect(meta?.batch).toBe(false);
126
+ expect(meta?.endpoint).toBe('/predict');
127
+ });
128
+ });
@@ -50,5 +50,5 @@ let MetricsService = class MetricsService {
50
50
  };
51
51
  exports.MetricsService = MetricsService;
52
52
  exports.MetricsService = MetricsService = __decorate([
53
- (0, core_1.Injectable)()
53
+ (0, core_1.Service)()
54
54
  ], MetricsService);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=metrics.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.service.test.d.ts","sourceRoot":"","sources":["../../src/evaluation/metrics.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const metrics_service_1 = require("./metrics.service");
4
+ describe('MetricsService', () => {
5
+ let service;
6
+ beforeEach(() => {
7
+ service = new metrics_service_1.MetricsService();
8
+ });
9
+ it('records and retrieves evaluation', () => {
10
+ service.recordEvaluation({
11
+ modelName: 'model',
12
+ version: '1.0.0',
13
+ metrics: { accuracy: 0.95, loss: 0.05 },
14
+ evaluatedAt: new Date(),
15
+ });
16
+ const metrics = service.getMetrics('model', '1.0.0');
17
+ expect(metrics?.metrics.accuracy).toBe(0.95);
18
+ });
19
+ it('getMetrics returns latest when no version', () => {
20
+ service.recordEvaluation({
21
+ modelName: 'm',
22
+ version: '1',
23
+ metrics: { accuracy: 0.9 },
24
+ evaluatedAt: new Date(),
25
+ });
26
+ service.recordEvaluation({
27
+ modelName: 'm',
28
+ version: '2',
29
+ metrics: { accuracy: 0.95 },
30
+ evaluatedAt: new Date(),
31
+ });
32
+ const latest = service.getMetrics('m');
33
+ expect(latest?.version).toBe('2');
34
+ expect(latest?.metrics.accuracy).toBe(0.95);
35
+ });
36
+ it('getHistory returns all evaluations', () => {
37
+ service.recordEvaluation({
38
+ modelName: 'm',
39
+ version: '1',
40
+ metrics: {},
41
+ evaluatedAt: new Date(),
42
+ });
43
+ service.recordEvaluation({
44
+ modelName: 'm',
45
+ version: '2',
46
+ metrics: {},
47
+ evaluatedAt: new Date(),
48
+ });
49
+ expect(service.getHistory('m')).toHaveLength(2);
50
+ });
51
+ it('compareVersions returns winner by accuracy', () => {
52
+ service.recordEvaluation({
53
+ modelName: 'm',
54
+ version: 'a',
55
+ metrics: { accuracy: 0.9 },
56
+ evaluatedAt: new Date(),
57
+ });
58
+ service.recordEvaluation({
59
+ modelName: 'm',
60
+ version: 'b',
61
+ metrics: { accuracy: 0.95 },
62
+ evaluatedAt: new Date(),
63
+ });
64
+ const { a, b, winner } = service.compareVersions('m', 'a', 'b');
65
+ expect(a?.metrics.accuracy).toBe(0.9);
66
+ expect(b?.metrics.accuracy).toBe(0.95);
67
+ expect(winner).toBe('b');
68
+ });
69
+ it('compareVersions when no accuracy', () => {
70
+ service.recordEvaluation({
71
+ modelName: 'm',
72
+ version: 'a',
73
+ metrics: {},
74
+ evaluatedAt: new Date(),
75
+ });
76
+ const { winner } = service.compareVersions('m', 'a', 'b');
77
+ expect(winner).toBeUndefined();
78
+ });
79
+ });
@@ -42,6 +42,6 @@ let BatchService = class BatchService {
42
42
  };
43
43
  exports.BatchService = BatchService;
44
44
  exports.BatchService = BatchService = __decorate([
45
- (0, core_1.Injectable)(),
45
+ (0, core_1.Service)(),
46
46
  __metadata("design:paramtypes", [predictor_service_1.PredictorService])
47
47
  ], BatchService);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=batch.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.service.test.d.ts","sourceRoot":"","sources":["../../src/inference/batch.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const model_registry_1 = require("../registry/model.registry");
13
+ const predictor_service_1 = require("./predictor.service");
14
+ const batch_service_1 = require("./batch.service");
15
+ const decorators_1 = require("../decorators");
16
+ describe('BatchService', () => {
17
+ let batchService;
18
+ let BatchModel = class BatchModel {
19
+ train() { }
20
+ async predict(input) {
21
+ return { value: input * 2 };
22
+ }
23
+ };
24
+ __decorate([
25
+ (0, decorators_1.Train)(),
26
+ __metadata("design:type", Function),
27
+ __metadata("design:paramtypes", []),
28
+ __metadata("design:returntype", void 0)
29
+ ], BatchModel.prototype, "train", null);
30
+ __decorate([
31
+ (0, decorators_1.Predict)(),
32
+ __metadata("design:type", Function),
33
+ __metadata("design:paramtypes", [Object]),
34
+ __metadata("design:returntype", Promise)
35
+ ], BatchModel.prototype, "predict", null);
36
+ BatchModel = __decorate([
37
+ (0, decorators_1.Model)({ name: 'batch-model', version: '1.0.0', framework: 'tensorflow' })
38
+ ], BatchModel);
39
+ beforeEach(() => {
40
+ const registry = new model_registry_1.ModelRegistry();
41
+ registry.register({
42
+ metadata: { name: 'batch-model', version: '1.0.0', framework: 'tensorflow' },
43
+ instance: new BatchModel(),
44
+ predictMethod: 'predict',
45
+ });
46
+ batchService = new batch_service_1.BatchService(new predictor_service_1.PredictorService(registry));
47
+ });
48
+ it('processes batch of inputs', async () => {
49
+ const results = await batchService.predictBatch('batch-model', [1, 2, 3]);
50
+ expect(results).toEqual([{ value: 2 }, { value: 4 }, { value: 6 }]);
51
+ });
52
+ it('respects batchSize option', async () => {
53
+ const results = await batchService.predictBatch('batch-model', [1, 2, 3, 4, 5], {
54
+ batchSize: 2,
55
+ });
56
+ expect(results).toHaveLength(5);
57
+ });
58
+ it('throws when model not found', async () => {
59
+ await expect(batchService.predictBatch('unknown', [1])).rejects.toThrow('Model not found: unknown');
60
+ });
61
+ });
@@ -60,6 +60,6 @@ let PredictorService = class PredictorService {
60
60
  };
61
61
  exports.PredictorService = PredictorService;
62
62
  exports.PredictorService = PredictorService = __decorate([
63
- (0, core_1.Injectable)(),
63
+ (0, core_1.Service)(),
64
64
  __metadata("design:paramtypes", [model_registry_1.ModelRegistry])
65
65
  ], PredictorService);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=predictor.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predictor.service.test.d.ts","sourceRoot":"","sources":["../../src/inference/predictor.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const model_registry_1 = require("../registry/model.registry");
13
+ const predictor_service_1 = require("./predictor.service");
14
+ const decorators_1 = require("../decorators");
15
+ describe('PredictorService', () => {
16
+ let registry;
17
+ let predictor;
18
+ let TestModel = class TestModel {
19
+ async train() {
20
+ return { accuracy: 0.9 };
21
+ }
22
+ async predict(input) {
23
+ return { sentiment: 'positive', score: 0.92, input };
24
+ }
25
+ };
26
+ __decorate([
27
+ (0, decorators_1.Train)(),
28
+ __metadata("design:type", Function),
29
+ __metadata("design:paramtypes", []),
30
+ __metadata("design:returntype", Promise)
31
+ ], TestModel.prototype, "train", null);
32
+ __decorate([
33
+ (0, decorators_1.Predict)(),
34
+ __metadata("design:type", Function),
35
+ __metadata("design:paramtypes", [Object]),
36
+ __metadata("design:returntype", Promise)
37
+ ], TestModel.prototype, "predict", null);
38
+ TestModel = __decorate([
39
+ (0, decorators_1.Model)({ name: 'test-model', version: '1.0.0', framework: 'tensorflow' })
40
+ ], TestModel);
41
+ beforeEach(() => {
42
+ registry = new model_registry_1.ModelRegistry();
43
+ predictor = new predictor_service_1.PredictorService(registry);
44
+ registry.register({
45
+ metadata: { name: 'test-model', version: '1.0.0', framework: 'tensorflow' },
46
+ instance: new TestModel(),
47
+ trainMethod: 'train',
48
+ predictMethod: 'predict',
49
+ });
50
+ });
51
+ it('predicts and returns result', async () => {
52
+ const result = await predictor.predict('test-model', 'hello');
53
+ expect(result).toEqual({ sentiment: 'positive', score: 0.92, input: 'hello' });
54
+ });
55
+ it('predicts with specific version', async () => {
56
+ const result = await predictor.predict('test-model', 'hi', '1.0.0');
57
+ expect(result.sentiment).toBe('positive');
58
+ });
59
+ it('throws when model not found', async () => {
60
+ await expect(predictor.predict('unknown', {})).rejects.toThrow('Model not found: unknown');
61
+ });
62
+ it('throws when model has no prediction method', async () => {
63
+ let NoPredictModel = class NoPredictModel {
64
+ train() { }
65
+ };
66
+ __decorate([
67
+ (0, decorators_1.Train)(),
68
+ __metadata("design:type", Function),
69
+ __metadata("design:paramtypes", []),
70
+ __metadata("design:returntype", void 0)
71
+ ], NoPredictModel.prototype, "train", null);
72
+ NoPredictModel = __decorate([
73
+ (0, decorators_1.Model)({ name: 'no-predict', version: '1.0.0', framework: 'tensorflow' })
74
+ ], NoPredictModel);
75
+ registry.register({
76
+ metadata: { name: 'no-predict', version: '1.0.0', framework: 'tensorflow' },
77
+ instance: new NoPredictModel(),
78
+ trainMethod: 'train',
79
+ });
80
+ await expect(predictor.predict('no-predict', {})).rejects.toThrow('no-predict has no prediction method');
81
+ });
82
+ it('discoverPredictMethod finds @Predict decorated method', () => {
83
+ const instance = new TestModel();
84
+ expect(predictor.discoverPredictMethod(instance)).toBe('predict');
85
+ });
86
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ml-model.base.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ml-model.base.test.d.ts","sourceRoot":"","sources":["../src/ml-model.base.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const ml_model_base_1 = require("./ml-model.base");
13
+ const model_registry_1 = require("./registry/model.registry");
14
+ const trainer_service_1 = require("./training/trainer.service");
15
+ const predictor_service_1 = require("./inference/predictor.service");
16
+ const decorators_1 = require("./decorators");
17
+ let RegisterTestModel = class RegisterTestModel {
18
+ train() { }
19
+ predict() { }
20
+ };
21
+ __decorate([
22
+ (0, decorators_1.Train)(),
23
+ __metadata("design:type", Function),
24
+ __metadata("design:paramtypes", []),
25
+ __metadata("design:returntype", void 0)
26
+ ], RegisterTestModel.prototype, "train", null);
27
+ __decorate([
28
+ (0, decorators_1.Predict)(),
29
+ __metadata("design:type", Function),
30
+ __metadata("design:paramtypes", []),
31
+ __metadata("design:returntype", void 0)
32
+ ], RegisterTestModel.prototype, "predict", null);
33
+ RegisterTestModel = __decorate([
34
+ (0, decorators_1.Model)({ name: 'register-test', version: '1.0.0', framework: 'tensorflow' })
35
+ ], RegisterTestModel);
36
+ describe('registerMLModel', () => {
37
+ it('registers model with registry', () => {
38
+ const registry = new model_registry_1.ModelRegistry();
39
+ const trainer = new trainer_service_1.TrainerService(registry);
40
+ const predictor = new predictor_service_1.PredictorService(registry);
41
+ const instance = new RegisterTestModel();
42
+ (0, ml_model_base_1.registerMLModel)(instance, registry, trainer, predictor);
43
+ const registered = registry.get('register-test', '1.0.0');
44
+ expect(registered).toBeDefined();
45
+ expect(registered?.trainMethod).toBe('train');
46
+ expect(registered?.predictMethod).toBe('predict');
47
+ });
48
+ it('does nothing for class without @Model', () => {
49
+ const registry = new model_registry_1.ModelRegistry();
50
+ const trainer = new trainer_service_1.TrainerService(registry);
51
+ const predictor = new predictor_service_1.PredictorService(registry);
52
+ class PlainClass {
53
+ }
54
+ (0, ml_model_base_1.registerMLModel)(new PlainClass(), registry, trainer, predictor);
55
+ expect(registry.list()).toHaveLength(0);
56
+ });
57
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ml.module.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ml.module.test.d.ts","sourceRoot":"","sources":["../src/ml.module.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const ml_module_1 = require("./ml.module");
13
+ const decorators_1 = require("./decorators");
14
+ let TestModel = class TestModel {
15
+ train() { }
16
+ predict() { }
17
+ };
18
+ __decorate([
19
+ (0, decorators_1.Train)(),
20
+ __metadata("design:type", Function),
21
+ __metadata("design:paramtypes", []),
22
+ __metadata("design:returntype", void 0)
23
+ ], TestModel.prototype, "train", null);
24
+ __decorate([
25
+ (0, decorators_1.Predict)(),
26
+ __metadata("design:type", Function),
27
+ __metadata("design:paramtypes", []),
28
+ __metadata("design:returntype", void 0)
29
+ ], TestModel.prototype, "predict", null);
30
+ TestModel = __decorate([
31
+ (0, decorators_1.Model)({ name: 'test', version: '1.0.0', framework: 'tensorflow' })
32
+ ], TestModel);
33
+ describe('MLModule', () => {
34
+ describe('forRoot', () => {
35
+ it('returns dynamic module structure', () => {
36
+ const result = ml_module_1.MLModule.forRoot();
37
+ expect(result).toHaveProperty('module', ml_module_1.MLModule);
38
+ expect(result).toHaveProperty('providers');
39
+ expect(result).toHaveProperty('exports');
40
+ expect(Array.isArray(result.providers)).toBe(true);
41
+ expect(result.providers.length).toBeGreaterThan(0);
42
+ });
43
+ it('includes models in providers when specified', () => {
44
+ const result = ml_module_1.MLModule.forRoot({ models: [TestModel] });
45
+ expect(result.providers).toContain(TestModel);
46
+ });
47
+ it('includes ML_MODELS and MLModelBootstrap when models provided', () => {
48
+ const result = ml_module_1.MLModule.forRoot({ models: [TestModel] });
49
+ const mlModels = result.providers.find((p) => p && typeof p === 'object' && 'provide' in p);
50
+ expect(mlModels).toBeDefined();
51
+ });
52
+ });
53
+ describe('getOptions', () => {
54
+ it('returns last forRoot options', () => {
55
+ ml_module_1.MLModule.forRoot({ models: [TestModel] });
56
+ const opts = ml_module_1.MLModule.getOptions();
57
+ expect(opts.models).toContain(TestModel);
58
+ });
59
+ });
60
+ });
@@ -60,5 +60,5 @@ let ModelRegistry = class ModelRegistry {
60
60
  };
61
61
  exports.ModelRegistry = ModelRegistry;
62
62
  exports.ModelRegistry = ModelRegistry = __decorate([
63
- (0, core_1.Injectable)()
63
+ (0, core_1.Service)()
64
64
  ], ModelRegistry);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=model.registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.registry.test.d.ts","sourceRoot":"","sources":["../../src/registry/model.registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const model_registry_1 = require("./model.registry");
4
+ describe('ModelRegistry', () => {
5
+ let registry;
6
+ beforeEach(() => {
7
+ registry = new model_registry_1.ModelRegistry();
8
+ });
9
+ it('registers and retrieves model by name and version', () => {
10
+ const model = {
11
+ metadata: { name: 'sentiment', version: '1.0.0', framework: 'tensorflow' },
12
+ instance: {},
13
+ trainMethod: 'train',
14
+ predictMethod: 'predict',
15
+ };
16
+ registry.register(model);
17
+ const retrieved = registry.get('sentiment', '1.0.0');
18
+ expect(retrieved).toBe(model);
19
+ });
20
+ it('returns latest version when version not specified', () => {
21
+ const v1 = {
22
+ metadata: { name: 'model', version: '1.0.0', framework: 'tensorflow' },
23
+ instance: {},
24
+ };
25
+ const v2 = {
26
+ metadata: { name: 'model', version: '2.0.0', framework: 'tensorflow' },
27
+ instance: {},
28
+ };
29
+ registry.register(v1);
30
+ registry.register(v2);
31
+ expect(registry.get('model')).toBe(v2);
32
+ expect(registry.get('model', '1.0.0')).toBe(v1);
33
+ });
34
+ it('returns undefined for unknown model', () => {
35
+ expect(registry.get('unknown')).toBeUndefined();
36
+ expect(registry.get('unknown', '1.0.0')).toBeUndefined();
37
+ });
38
+ it('lists all registered models', () => {
39
+ registry.register({
40
+ metadata: { name: 'a', version: '1', framework: 'tensorflow' },
41
+ instance: {},
42
+ });
43
+ registry.register({
44
+ metadata: { name: 'b', version: '1', framework: 'onnx' },
45
+ instance: {},
46
+ });
47
+ const list = registry.list();
48
+ expect(list).toHaveLength(2);
49
+ expect(list.map((m) => m.name)).toContain('a');
50
+ expect(list.map((m) => m.name)).toContain('b');
51
+ });
52
+ it('getVersions returns version history', () => {
53
+ registry.register({
54
+ metadata: { name: 'model', version: '1.0.0', framework: 'tensorflow' },
55
+ instance: {},
56
+ });
57
+ registry.register({
58
+ metadata: { name: 'model', version: '2.0.0', framework: 'tensorflow' },
59
+ instance: {},
60
+ });
61
+ const versions = registry.getVersions('model');
62
+ expect(versions).toHaveLength(2);
63
+ expect(versions.map((v) => v.version)).toEqual(['1.0.0', '2.0.0']);
64
+ });
65
+ it('unregister removes model', () => {
66
+ registry.register({
67
+ metadata: { name: 'model', version: '1.0.0', framework: 'tensorflow' },
68
+ instance: {},
69
+ });
70
+ expect(registry.get('model', '1.0.0')).toBeDefined();
71
+ const deleted = registry.unregister('model', '1.0.0');
72
+ expect(deleted).toBe(true);
73
+ expect(registry.get('model', '1.0.0')).toBeUndefined();
74
+ expect(registry.unregister('model', '1.0.0')).toBe(false);
75
+ });
76
+ });
@@ -45,5 +45,5 @@ let PipelineService = class PipelineService {
45
45
  };
46
46
  exports.PipelineService = PipelineService;
47
47
  exports.PipelineService = PipelineService = __decorate([
48
- (0, core_1.Injectable)()
48
+ (0, core_1.Service)()
49
49
  ], PipelineService);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pipeline.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.service.test.d.ts","sourceRoot":"","sources":["../../src/training/pipeline.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pipeline_service_1 = require("./pipeline.service");
4
+ describe('PipelineService', () => {
5
+ let service;
6
+ beforeEach(() => {
7
+ service = new pipeline_service_1.PipelineService();
8
+ });
9
+ it('registers and runs pipeline', async () => {
10
+ service.registerPipeline('test', [
11
+ { name: 'step1', transform: (d) => ({ ...d, a: 1 }) },
12
+ { name: 'step2', transform: (d) => ({ ...d, b: 2 }) },
13
+ ]);
14
+ const result = await service.run('test', { x: 0 });
15
+ expect(result).toEqual({ x: 0, a: 1, b: 2 });
16
+ });
17
+ it('handles async transforms', async () => {
18
+ service.registerPipeline('async', [
19
+ { name: 'async', transform: async (d) => ({ ...d, done: true }) },
20
+ ]);
21
+ const result = await service.run('async', {});
22
+ expect(result).toEqual({ done: true });
23
+ });
24
+ it('throws when pipeline not found', async () => {
25
+ await expect(service.run('missing', {})).rejects.toThrow('Pipeline not found: missing');
26
+ });
27
+ it('getPipeline returns steps', () => {
28
+ const steps = [{ name: 's1', transform: (d) => d }];
29
+ service.registerPipeline('p', steps);
30
+ expect(service.getPipeline('p')).toEqual(steps);
31
+ expect(service.getPipeline('x')).toBeUndefined();
32
+ });
33
+ it('listPipelines returns names', () => {
34
+ service.registerPipeline('a', []);
35
+ service.registerPipeline('b', []);
36
+ expect(service.listPipelines()).toEqual(['a', 'b']);
37
+ });
38
+ });
@@ -64,6 +64,6 @@ let TrainerService = class TrainerService {
64
64
  };
65
65
  exports.TrainerService = TrainerService;
66
66
  exports.TrainerService = TrainerService = __decorate([
67
- (0, core_1.Injectable)(),
67
+ (0, core_1.Service)(),
68
68
  __metadata("design:paramtypes", [model_registry_1.ModelRegistry])
69
69
  ], TrainerService);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=trainer.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trainer.service.test.d.ts","sourceRoot":"","sources":["../../src/training/trainer.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const model_registry_1 = require("../registry/model.registry");
13
+ const trainer_service_1 = require("./trainer.service");
14
+ const decorators_1 = require("../decorators");
15
+ describe('TrainerService', () => {
16
+ let registry;
17
+ let trainer;
18
+ let TestModel = class TestModel {
19
+ async train(data) {
20
+ return { accuracy: 0.95, loss: 0.05, inputSize: data?.size };
21
+ }
22
+ async predict() {
23
+ return { value: 1 };
24
+ }
25
+ };
26
+ __decorate([
27
+ (0, decorators_1.Train)(),
28
+ __metadata("design:type", Function),
29
+ __metadata("design:paramtypes", [Object]),
30
+ __metadata("design:returntype", Promise)
31
+ ], TestModel.prototype, "train", null);
32
+ __decorate([
33
+ (0, decorators_1.Predict)(),
34
+ __metadata("design:type", Function),
35
+ __metadata("design:paramtypes", []),
36
+ __metadata("design:returntype", Promise)
37
+ ], TestModel.prototype, "predict", null);
38
+ TestModel = __decorate([
39
+ (0, decorators_1.Model)({ name: 'test-model', version: '1.0.0', framework: 'tensorflow' })
40
+ ], TestModel);
41
+ beforeEach(() => {
42
+ registry = new model_registry_1.ModelRegistry();
43
+ trainer = new trainer_service_1.TrainerService(registry);
44
+ const instance = new TestModel();
45
+ registry.register({
46
+ metadata: { name: 'test-model', version: '1.0.0', framework: 'tensorflow' },
47
+ instance,
48
+ trainMethod: 'train',
49
+ predictMethod: 'predict',
50
+ });
51
+ });
52
+ it('trains model and returns result', async () => {
53
+ const result = await trainer.train('test-model', { size: 100 });
54
+ expect(result).toEqual({ accuracy: 0.95, loss: 0.05, inputSize: 100 });
55
+ });
56
+ it('throws when model not found', async () => {
57
+ await expect(trainer.train('unknown', {})).rejects.toThrow('Model not found: unknown');
58
+ });
59
+ it('throws when model has no training method', async () => {
60
+ let NoTrainModel = class NoTrainModel {
61
+ predict() { }
62
+ };
63
+ __decorate([
64
+ (0, decorators_1.Predict)(),
65
+ __metadata("design:type", Function),
66
+ __metadata("design:paramtypes", []),
67
+ __metadata("design:returntype", void 0)
68
+ ], NoTrainModel.prototype, "predict", null);
69
+ NoTrainModel = __decorate([
70
+ (0, decorators_1.Model)({ name: 'no-train', version: '1.0.0', framework: 'tensorflow' })
71
+ ], NoTrainModel);
72
+ registry.register({
73
+ metadata: { name: 'no-train', version: '1.0.0', framework: 'tensorflow' },
74
+ instance: new NoTrainModel(),
75
+ predictMethod: 'predict',
76
+ });
77
+ await expect(trainer.train('no-train', {})).rejects.toThrow('no-train has no training method');
78
+ });
79
+ it('discoverTrainMethod finds @Train decorated method', () => {
80
+ const instance = new TestModel();
81
+ expect(trainer.discoverTrainMethod(instance)).toBe('train');
82
+ });
83
+ it('discoverTrainMethod returns undefined for class without @Model', () => {
84
+ class PlainClass {
85
+ train() { }
86
+ }
87
+ expect(trainer.discoverTrainMethod(new PlainClass())).toBeUndefined();
88
+ });
89
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/ml",
3
- "version": "0.2.0-beta.54",
3
+ "version": "0.2.0-beta.55",
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.com",
51
- "gitHead": "c593ce33447cdc62d7bd2386cc2db47840292fcb"
51
+ "gitHead": "f2e54f346eea552595a44607999454a9e388cb9e"
52
52
  }