QuickLab-MCP 0.1.0__py3-none-any.whl

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 (92) hide show
  1. ml_mcp/__init__.py +3 -0
  2. ml_mcp/application/__init__.py +1 -0
  3. ml_mcp/application/analysis/__init__.py +15 -0
  4. ml_mcp/application/analysis/diagnostics.py +122 -0
  5. ml_mcp/application/analysis/service.py +155 -0
  6. ml_mcp/application/artifacts/__init__.py +5 -0
  7. ml_mcp/application/artifacts/service.py +149 -0
  8. ml_mcp/application/datasets/__init__.py +6 -0
  9. ml_mcp/application/datasets/service.py +178 -0
  10. ml_mcp/application/datasets/validator.py +156 -0
  11. ml_mcp/application/experiments/__init__.py +5 -0
  12. ml_mcp/application/experiments/service.py +254 -0
  13. ml_mcp/application/models/__init__.py +6 -0
  14. ml_mcp/application/models/catalog_seed.py +131 -0
  15. ml_mcp/application/models/service.py +177 -0
  16. ml_mcp/application/projects/__init__.py +5 -0
  17. ml_mcp/application/projects/service.py +93 -0
  18. ml_mcp/config/__init__.py +5 -0
  19. ml_mcp/config/settings.py +176 -0
  20. ml_mcp/domain/__init__.py +1 -0
  21. ml_mcp/domain/errors/__init__.py +39 -0
  22. ml_mcp/domain/errors/base.py +155 -0
  23. ml_mcp/domain/errors/codes.py +23 -0
  24. ml_mcp/domain/policies/__init__.py +16 -0
  25. ml_mcp/domain/policies/rbac.py +126 -0
  26. ml_mcp/domain/policies/tenant_isolation.py +34 -0
  27. ml_mcp/domain/policies/tool_policy.py +185 -0
  28. ml_mcp/domain/value_objects/__init__.py +41 -0
  29. ml_mcp/domain/value_objects/dataset.py +60 -0
  30. ml_mcp/domain/value_objects/experiment.py +131 -0
  31. ml_mcp/domain/value_objects/model.py +48 -0
  32. ml_mcp/infrastructure/__init__.py +1 -0
  33. ml_mcp/infrastructure/object_storage/__init__.py +5 -0
  34. ml_mcp/infrastructure/object_storage/s3.py +272 -0
  35. ml_mcp/infrastructure/postgres/__init__.py +14 -0
  36. ml_mcp/infrastructure/postgres/base.py +40 -0
  37. ml_mcp/infrastructure/postgres/models.py +356 -0
  38. ml_mcp/infrastructure/postgres/repositories/__init__.py +21 -0
  39. ml_mcp/infrastructure/postgres/repositories/artifacts.py +34 -0
  40. ml_mcp/infrastructure/postgres/repositories/audit.py +34 -0
  41. ml_mcp/infrastructure/postgres/repositories/datasets.py +85 -0
  42. ml_mcp/infrastructure/postgres/repositories/experiments.py +134 -0
  43. ml_mcp/infrastructure/postgres/repositories/metrics.py +61 -0
  44. ml_mcp/infrastructure/postgres/repositories/models.py +61 -0
  45. ml_mcp/infrastructure/postgres/repositories/outbox.py +47 -0
  46. ml_mcp/infrastructure/postgres/repositories/projects.py +83 -0
  47. ml_mcp/infrastructure/postgres/session.py +117 -0
  48. ml_mcp/infrastructure/queue/__init__.py +6 -0
  49. ml_mcp/infrastructure/queue/outbox_processor.py +67 -0
  50. ml_mcp/infrastructure/queue/task_queue.py +98 -0
  51. ml_mcp/infrastructure/redis/__init__.py +11 -0
  52. ml_mcp/infrastructure/redis/client.py +64 -0
  53. ml_mcp/infrastructure/redis/rate_limiter.py +129 -0
  54. ml_mcp/infrastructure/telemetry/__init__.py +23 -0
  55. ml_mcp/infrastructure/telemetry/logging.py +155 -0
  56. ml_mcp/infrastructure/telemetry/otel.py +141 -0
  57. ml_mcp/mcp/__init__.py +1 -0
  58. ml_mcp/mcp/prompts.py +38 -0
  59. ml_mcp/mcp/resources.py +35 -0
  60. ml_mcp/mcp/schemas/__init__.py +1 -0
  61. ml_mcp/mcp/schemas/tools.py +124 -0
  62. ml_mcp/mcp/tools/__init__.py +63 -0
  63. ml_mcp/mcp/tools/analysis.py +37 -0
  64. ml_mcp/mcp/tools/datasets.py +82 -0
  65. ml_mcp/mcp/tools/discovery.py +82 -0
  66. ml_mcp/mcp/tools/experiments.py +96 -0
  67. ml_mcp/mcp/tools/projects.py +39 -0
  68. ml_mcp/mcp/tools/results.py +59 -0
  69. ml_mcp/server/__init__.py +1 -0
  70. ml_mcp/server/app.py +488 -0
  71. ml_mcp/server/auth.py +146 -0
  72. ml_mcp/server/context.py +59 -0
  73. ml_mcp/server/http.py +183 -0
  74. ml_mcp/server/middleware.py +121 -0
  75. ml_mcp/server/stdio.py +42 -0
  76. ml_mcp/workers/__init__.py +1 -0
  77. ml_mcp/workers/daemon.py +130 -0
  78. ml_mcp/workers/metrics.py +110 -0
  79. ml_mcp/workers/preprocessing.py +170 -0
  80. ml_mcp/workers/runner.py +250 -0
  81. ml_mcp/workers/sandbox.py +35 -0
  82. ml_mcp/workers/trainers/__init__.py +52 -0
  83. ml_mcp/workers/trainers/base.py +52 -0
  84. ml_mcp/workers/trainers/catboost_model.py +85 -0
  85. ml_mcp/workers/trainers/lightgbm_model.py +81 -0
  86. ml_mcp/workers/trainers/sklearn_models.py +266 -0
  87. ml_mcp/workers/trainers/xgboost_model.py +82 -0
  88. quicklab_mcp-0.1.0.dist-info/METADATA +540 -0
  89. quicklab_mcp-0.1.0.dist-info/RECORD +92 -0
  90. quicklab_mcp-0.1.0.dist-info/WHEEL +5 -0
  91. quicklab_mcp-0.1.0.dist-info/entry_points.txt +4 -0
  92. quicklab_mcp-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,81 @@
1
+ """LightGBM model trainer."""
2
+
3
+ import io
4
+ from typing import Any
5
+
6
+ import joblib
7
+ import lightgbm as lgb
8
+ import numpy as np
9
+
10
+ from ml_mcp.domain.value_objects import TaskType
11
+ from ml_mcp.workers.metrics import MetricsCalculator
12
+ from ml_mcp.workers.trainers.base import BaseModelTrainer, TrainingResult
13
+
14
+
15
+ class LightGBMTrainer(BaseModelTrainer):
16
+ """Trainer for LightGBM gradient boosted trees."""
17
+
18
+ def train(
19
+ self,
20
+ X_tr: np.ndarray,
21
+ y_tr: np.ndarray,
22
+ X_val: np.ndarray,
23
+ y_val: np.ndarray,
24
+ task_type: TaskType,
25
+ hyperparameters: dict[str, Any],
26
+ random_seed: int = 42,
27
+ max_cpu_cores: int | None = None,
28
+ ) -> TrainingResult:
29
+ n_jobs = max_cpu_cores if max_cpu_cores is not None else hyperparameters.get("n_jobs", -1)
30
+ params = {
31
+ "random_state": random_seed,
32
+ "n_estimators": hyperparameters.get("n_estimators", 100),
33
+ "num_leaves": hyperparameters.get("num_leaves", 31),
34
+ "learning_rate": hyperparameters.get("learning_rate", 0.1),
35
+ "n_jobs": n_jobs,
36
+ "verbose": -1,
37
+ }
38
+
39
+ if task_type in (TaskType.BINARY_CLASSIFICATION, TaskType.MULTICLASS_CLASSIFICATION):
40
+ model = lgb.LGBMClassifier(**params)
41
+ model.fit(X_tr, y_tr, eval_set=[(X_val, y_val)])
42
+
43
+ train_pred = model.predict(X_tr)
44
+ train_proba = model.predict_proba(X_tr)
45
+ val_pred = model.predict(X_val)
46
+ val_proba = model.predict_proba(X_val)
47
+
48
+ train_metrics = MetricsCalculator.compute_classification_metrics(
49
+ y_tr, train_pred, train_proba, task_type
50
+ )
51
+ val_metrics = MetricsCalculator.compute_classification_metrics(
52
+ y_val, val_pred, val_proba, task_type
53
+ )
54
+ else:
55
+ model = lgb.LGBMRegressor(**params)
56
+ model.fit(X_tr, y_tr, eval_set=[(X_val, y_val)])
57
+
58
+ train_pred = model.predict(X_tr)
59
+ val_pred = model.predict(X_val)
60
+ val_proba = None
61
+
62
+ train_metrics = MetricsCalculator.compute_regression_metrics(y_tr, train_pred)
63
+ val_metrics = MetricsCalculator.compute_regression_metrics(y_val, val_pred)
64
+
65
+ feat_imp: dict[str, float] = {}
66
+ if hasattr(model, "feature_importances_"):
67
+ feat_imp = {f"feature_{i}": float(v) for i, v in enumerate(model.feature_importances_)}
68
+
69
+ bio = io.BytesIO()
70
+ joblib.dump(model, bio)
71
+
72
+ return TrainingResult(
73
+ model=model,
74
+ task_type=task_type,
75
+ train_metrics=train_metrics,
76
+ validation_metrics=val_metrics,
77
+ feature_importances=feat_imp,
78
+ val_predictions=val_pred,
79
+ val_probabilities=val_proba,
80
+ model_artifact_bytes=bio.getvalue(),
81
+ )
@@ -0,0 +1,266 @@
1
+ """Scikit-Learn model trainers: Logistic Regression, Random Forest, Linear SVM, and MLP."""
2
+
3
+ import io
4
+ from typing import Any
5
+
6
+ import joblib
7
+ import numpy as np
8
+ from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
9
+ from sklearn.linear_model import LogisticRegression
10
+ from sklearn.neural_network import MLPClassifier, MLPRegressor
11
+ from sklearn.svm import LinearSVC, LinearSVR
12
+
13
+ from ml_mcp.domain.value_objects import TaskType
14
+ from ml_mcp.workers.metrics import MetricsCalculator
15
+ from ml_mcp.workers.trainers.base import BaseModelTrainer, TrainingResult
16
+
17
+
18
+ class LogisticRegressionTrainer(BaseModelTrainer):
19
+ """Trainer for Logistic Regression."""
20
+
21
+ def train(
22
+ self,
23
+ X_tr: np.ndarray,
24
+ y_tr: np.ndarray,
25
+ X_val: np.ndarray,
26
+ y_val: np.ndarray,
27
+ task_type: TaskType,
28
+ hyperparameters: dict[str, Any],
29
+ random_seed: int = 42,
30
+ max_cpu_cores: int | None = None,
31
+ ) -> TrainingResult:
32
+ params: dict[str, Any] = {
33
+ "random_state": random_seed,
34
+ "max_iter": hyperparameters.get("max_iter", 200),
35
+ "C": hyperparameters.get("C", 1.0),
36
+ "penalty": hyperparameters.get("penalty", "l2"),
37
+ }
38
+ if "n_jobs" in hyperparameters:
39
+ params["n_jobs"] = hyperparameters["n_jobs"]
40
+ model = LogisticRegression(**params)
41
+ model.fit(X_tr, y_tr)
42
+
43
+ train_pred = model.predict(X_tr)
44
+ train_proba = model.predict_proba(X_tr)
45
+ val_pred = model.predict(X_val)
46
+ val_proba = model.predict_proba(X_val)
47
+
48
+ train_metrics = MetricsCalculator.compute_classification_metrics(
49
+ y_tr, train_pred, train_proba, task_type
50
+ )
51
+ val_metrics = MetricsCalculator.compute_classification_metrics(
52
+ y_val, val_pred, val_proba, task_type
53
+ )
54
+
55
+ # Feature importances from coefficients
56
+ feat_imp: dict[str, float] = {}
57
+ if hasattr(model, "coef_"):
58
+ coef = np.abs(model.coef_).mean(axis=0) if model.coef_.ndim > 1 else np.abs(model.coef_)
59
+ feat_imp = {f"feature_{i}": float(v) for i, v in enumerate(coef)}
60
+
61
+ bio = io.BytesIO()
62
+ joblib.dump(model, bio)
63
+
64
+ return TrainingResult(
65
+ model=model,
66
+ task_type=task_type,
67
+ train_metrics=train_metrics,
68
+ validation_metrics=val_metrics,
69
+ feature_importances=feat_imp,
70
+ val_predictions=val_pred,
71
+ val_probabilities=val_proba,
72
+ model_artifact_bytes=bio.getvalue(),
73
+ )
74
+
75
+
76
+ class RandomForestTrainer(BaseModelTrainer):
77
+ """Trainer for Random Forest classification and regression."""
78
+
79
+ def train(
80
+ self,
81
+ X_tr: np.ndarray,
82
+ y_tr: np.ndarray,
83
+ X_val: np.ndarray,
84
+ y_val: np.ndarray,
85
+ task_type: TaskType,
86
+ hyperparameters: dict[str, Any],
87
+ random_seed: int = 42,
88
+ max_cpu_cores: int | None = None,
89
+ ) -> TrainingResult:
90
+ n_jobs = max_cpu_cores if max_cpu_cores is not None else hyperparameters.get("n_jobs", -1)
91
+ params = {
92
+ "random_state": random_seed,
93
+ "n_estimators": hyperparameters.get("n_estimators", 100),
94
+ "max_depth": hyperparameters.get("max_depth", 10),
95
+ "min_samples_split": hyperparameters.get("min_samples_split", 2),
96
+ "n_jobs": n_jobs,
97
+ }
98
+
99
+ if task_type in (TaskType.BINARY_CLASSIFICATION, TaskType.MULTICLASS_CLASSIFICATION):
100
+ model = RandomForestClassifier(**params)
101
+ model.fit(X_tr, y_tr)
102
+
103
+ train_pred = model.predict(X_tr)
104
+ train_proba = model.predict_proba(X_tr)
105
+ val_pred = model.predict(X_val)
106
+ val_proba = model.predict_proba(X_val)
107
+
108
+ train_metrics = MetricsCalculator.compute_classification_metrics(
109
+ y_tr, train_pred, train_proba, task_type
110
+ )
111
+ val_metrics = MetricsCalculator.compute_classification_metrics(
112
+ y_val, val_pred, val_proba, task_type
113
+ )
114
+ else:
115
+ model = RandomForestRegressor(**params)
116
+ model.fit(X_tr, y_tr)
117
+
118
+ train_pred = model.predict(X_tr)
119
+ val_pred = model.predict(X_val)
120
+ val_proba = None
121
+
122
+ train_metrics = MetricsCalculator.compute_regression_metrics(y_tr, train_pred)
123
+ val_metrics = MetricsCalculator.compute_regression_metrics(y_val, val_pred)
124
+
125
+ feat_imp: dict[str, float] = {}
126
+ if hasattr(model, "feature_importances_"):
127
+ feat_imp = {f"feature_{i}": float(v) for i, v in enumerate(model.feature_importances_)}
128
+
129
+ bio = io.BytesIO()
130
+ joblib.dump(model, bio)
131
+
132
+ return TrainingResult(
133
+ model=model,
134
+ task_type=task_type,
135
+ train_metrics=train_metrics,
136
+ validation_metrics=val_metrics,
137
+ feature_importances=feat_imp,
138
+ val_predictions=val_pred,
139
+ val_probabilities=val_proba,
140
+ model_artifact_bytes=bio.getvalue(),
141
+ )
142
+
143
+
144
+ class LinearSVMTrainer(BaseModelTrainer):
145
+ """Trainer for Linear SVM."""
146
+
147
+ def train(
148
+ self,
149
+ X_tr: np.ndarray,
150
+ y_tr: np.ndarray,
151
+ X_val: np.ndarray,
152
+ y_val: np.ndarray,
153
+ task_type: TaskType,
154
+ hyperparameters: dict[str, Any],
155
+ random_seed: int = 42,
156
+ max_cpu_cores: int | None = None,
157
+ ) -> TrainingResult:
158
+ params = {
159
+ "random_state": random_seed,
160
+ "C": hyperparameters.get("C", 1.0),
161
+ "max_iter": hyperparameters.get("max_iter", 1000),
162
+ }
163
+
164
+ if task_type in (TaskType.BINARY_CLASSIFICATION, TaskType.MULTICLASS_CLASSIFICATION):
165
+ model = LinearSVC(**params)
166
+ model.fit(X_tr, y_tr)
167
+
168
+ train_pred = model.predict(X_tr)
169
+ val_pred = model.predict(X_val)
170
+ train_metrics = MetricsCalculator.compute_classification_metrics(
171
+ y_tr, train_pred, None, task_type
172
+ )
173
+ val_metrics = MetricsCalculator.compute_classification_metrics(
174
+ y_val, val_pred, None, task_type
175
+ )
176
+ val_proba = None
177
+ else:
178
+ model = LinearSVR(**params)
179
+ model.fit(X_tr, y_tr)
180
+
181
+ train_pred = model.predict(X_tr)
182
+ val_pred = model.predict(X_val)
183
+ train_metrics = MetricsCalculator.compute_regression_metrics(y_tr, train_pred)
184
+ val_metrics = MetricsCalculator.compute_regression_metrics(y_val, val_pred)
185
+ val_proba = None
186
+
187
+ feat_imp: dict[str, float] = {}
188
+ if hasattr(model, "coef_"):
189
+ coef = np.abs(model.coef_).mean(axis=0) if model.coef_.ndim > 1 else np.abs(model.coef_)
190
+ feat_imp = {f"feature_{i}": float(v) for i, v in enumerate(coef)}
191
+
192
+ bio = io.BytesIO()
193
+ joblib.dump(model, bio)
194
+
195
+ return TrainingResult(
196
+ model=model,
197
+ task_type=task_type,
198
+ train_metrics=train_metrics,
199
+ validation_metrics=val_metrics,
200
+ feature_importances=feat_imp,
201
+ val_predictions=val_pred,
202
+ val_probabilities=val_proba,
203
+ model_artifact_bytes=bio.getvalue(),
204
+ )
205
+
206
+
207
+ class MLPTrainer(BaseModelTrainer):
208
+ """Trainer for Multi-Layer Perceptron (neural network)."""
209
+
210
+ def train(
211
+ self,
212
+ X_tr: np.ndarray,
213
+ y_tr: np.ndarray,
214
+ X_val: np.ndarray,
215
+ y_val: np.ndarray,
216
+ task_type: TaskType,
217
+ hyperparameters: dict[str, Any],
218
+ random_seed: int = 42,
219
+ max_cpu_cores: int | None = None,
220
+ ) -> TrainingResult:
221
+ params = {
222
+ "random_state": random_seed,
223
+ "alpha": hyperparameters.get("alpha", 0.0001),
224
+ "max_iter": hyperparameters.get("max_iter", 200),
225
+ "hidden_layer_sizes": hyperparameters.get("hidden_layer_sizes", (100,)),
226
+ }
227
+
228
+ if task_type in (TaskType.BINARY_CLASSIFICATION, TaskType.MULTICLASS_CLASSIFICATION):
229
+ model = MLPClassifier(**params)
230
+ model.fit(X_tr, y_tr)
231
+
232
+ train_pred = model.predict(X_tr)
233
+ train_proba = model.predict_proba(X_tr)
234
+ val_pred = model.predict(X_val)
235
+ val_proba = model.predict_proba(X_val)
236
+
237
+ train_metrics = MetricsCalculator.compute_classification_metrics(
238
+ y_tr, train_pred, train_proba, task_type
239
+ )
240
+ val_metrics = MetricsCalculator.compute_classification_metrics(
241
+ y_val, val_pred, val_proba, task_type
242
+ )
243
+ else:
244
+ model = MLPRegressor(**params)
245
+ model.fit(X_tr, y_tr)
246
+
247
+ train_pred = model.predict(X_tr)
248
+ val_pred = model.predict(X_val)
249
+ val_proba = None
250
+
251
+ train_metrics = MetricsCalculator.compute_regression_metrics(y_tr, train_pred)
252
+ val_metrics = MetricsCalculator.compute_regression_metrics(y_val, val_pred)
253
+
254
+ bio = io.BytesIO()
255
+ joblib.dump(model, bio)
256
+
257
+ return TrainingResult(
258
+ model=model,
259
+ task_type=task_type,
260
+ train_metrics=train_metrics,
261
+ validation_metrics=val_metrics,
262
+ feature_importances={},
263
+ val_predictions=val_pred,
264
+ val_probabilities=val_proba,
265
+ model_artifact_bytes=bio.getvalue(),
266
+ )
@@ -0,0 +1,82 @@
1
+ """XGBoost model trainer."""
2
+
3
+ import io
4
+ from typing import Any
5
+
6
+ import joblib
7
+ import numpy as np
8
+ import xgboost as xgb
9
+
10
+ from ml_mcp.domain.value_objects import TaskType
11
+ from ml_mcp.workers.metrics import MetricsCalculator
12
+ from ml_mcp.workers.trainers.base import BaseModelTrainer, TrainingResult
13
+
14
+
15
+ class XGBoostTrainer(BaseModelTrainer):
16
+ """Trainer for XGBoost gradient boosted trees."""
17
+
18
+ def train(
19
+ self,
20
+ X_tr: np.ndarray,
21
+ y_tr: np.ndarray,
22
+ X_val: np.ndarray,
23
+ y_val: np.ndarray,
24
+ task_type: TaskType,
25
+ hyperparameters: dict[str, Any],
26
+ random_seed: int = 42,
27
+ max_cpu_cores: int | None = None,
28
+ ) -> TrainingResult:
29
+ n_jobs = max_cpu_cores if max_cpu_cores is not None else hyperparameters.get("n_jobs", -1)
30
+ params = {
31
+ "random_state": random_seed,
32
+ "n_estimators": hyperparameters.get("n_estimators", 100),
33
+ "max_depth": hyperparameters.get("max_depth", 6),
34
+ "learning_rate": hyperparameters.get("learning_rate", 0.1),
35
+ "subsample": hyperparameters.get("subsample", 1.0),
36
+ "n_jobs": n_jobs,
37
+ "verbosity": 0,
38
+ }
39
+
40
+ if task_type in (TaskType.BINARY_CLASSIFICATION, TaskType.MULTICLASS_CLASSIFICATION):
41
+ model = xgb.XGBClassifier(**params)
42
+ model.fit(X_tr, y_tr, eval_set=[(X_val, y_val)], verbose=False)
43
+
44
+ train_pred = model.predict(X_tr)
45
+ train_proba = model.predict_proba(X_tr)
46
+ val_pred = model.predict(X_val)
47
+ val_proba = model.predict_proba(X_val)
48
+
49
+ train_metrics = MetricsCalculator.compute_classification_metrics(
50
+ y_tr, train_pred, train_proba, task_type
51
+ )
52
+ val_metrics = MetricsCalculator.compute_classification_metrics(
53
+ y_val, val_pred, val_proba, task_type
54
+ )
55
+ else:
56
+ model = xgb.XGBRegressor(**params)
57
+ model.fit(X_tr, y_tr, eval_set=[(X_val, y_val)], verbose=False)
58
+
59
+ train_pred = model.predict(X_tr)
60
+ val_pred = model.predict(X_val)
61
+ val_proba = None
62
+
63
+ train_metrics = MetricsCalculator.compute_regression_metrics(y_tr, train_pred)
64
+ val_metrics = MetricsCalculator.compute_regression_metrics(y_val, val_pred)
65
+
66
+ feat_imp: dict[str, float] = {}
67
+ if hasattr(model, "feature_importances_"):
68
+ feat_imp = {f"feature_{i}": float(v) for i, v in enumerate(model.feature_importances_)}
69
+
70
+ bio = io.BytesIO()
71
+ joblib.dump(model, bio)
72
+
73
+ return TrainingResult(
74
+ model=model,
75
+ task_type=task_type,
76
+ train_metrics=train_metrics,
77
+ validation_metrics=val_metrics,
78
+ feature_importances=feat_imp,
79
+ val_predictions=val_pred,
80
+ val_probabilities=val_proba,
81
+ model_artifact_bytes=bio.getvalue(),
82
+ )