flowyml 1.7.2__py3-none-any.whl → 1.8.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.
- flowyml/assets/base.py +15 -0
- flowyml/assets/metrics.py +5 -0
- flowyml/cli/main.py +709 -0
- flowyml/cli/stack_cli.py +138 -25
- flowyml/core/__init__.py +17 -0
- flowyml/core/executor.py +161 -26
- flowyml/core/image_builder.py +129 -0
- flowyml/core/log_streamer.py +227 -0
- flowyml/core/orchestrator.py +22 -2
- flowyml/core/pipeline.py +34 -10
- flowyml/core/routing.py +558 -0
- flowyml/core/step.py +9 -1
- flowyml/core/step_grouping.py +49 -35
- flowyml/core/types.py +407 -0
- flowyml/monitoring/alerts.py +10 -0
- flowyml/monitoring/notifications.py +104 -25
- flowyml/monitoring/slack_blocks.py +323 -0
- flowyml/plugins/__init__.py +251 -0
- flowyml/plugins/alerters/__init__.py +1 -0
- flowyml/plugins/alerters/slack.py +168 -0
- flowyml/plugins/base.py +752 -0
- flowyml/plugins/config.py +478 -0
- flowyml/plugins/deployers/__init__.py +22 -0
- flowyml/plugins/deployers/gcp_cloud_run.py +200 -0
- flowyml/plugins/deployers/sagemaker.py +306 -0
- flowyml/plugins/deployers/vertex.py +290 -0
- flowyml/plugins/integration.py +369 -0
- flowyml/plugins/manager.py +510 -0
- flowyml/plugins/model_registries/__init__.py +22 -0
- flowyml/plugins/model_registries/mlflow.py +159 -0
- flowyml/plugins/model_registries/sagemaker.py +489 -0
- flowyml/plugins/model_registries/vertex.py +386 -0
- flowyml/plugins/orchestrators/__init__.py +13 -0
- flowyml/plugins/orchestrators/sagemaker.py +443 -0
- flowyml/plugins/orchestrators/vertex_ai.py +461 -0
- flowyml/plugins/registries/__init__.py +13 -0
- flowyml/plugins/registries/ecr.py +321 -0
- flowyml/plugins/registries/gcr.py +313 -0
- flowyml/plugins/registry.py +454 -0
- flowyml/plugins/stack.py +494 -0
- flowyml/plugins/stack_config.py +537 -0
- flowyml/plugins/stores/__init__.py +13 -0
- flowyml/plugins/stores/gcs.py +460 -0
- flowyml/plugins/stores/s3.py +453 -0
- flowyml/plugins/trackers/__init__.py +11 -0
- flowyml/plugins/trackers/mlflow.py +316 -0
- flowyml/plugins/validators/__init__.py +3 -0
- flowyml/plugins/validators/deepchecks.py +119 -0
- flowyml/registry/__init__.py +2 -1
- flowyml/registry/model_environment.py +109 -0
- flowyml/registry/model_registry.py +241 -96
- flowyml/serving/__init__.py +17 -0
- flowyml/serving/model_server.py +628 -0
- flowyml/stacks/__init__.py +60 -0
- flowyml/stacks/aws.py +93 -0
- flowyml/stacks/base.py +62 -0
- flowyml/stacks/components.py +12 -0
- flowyml/stacks/gcp.py +44 -9
- flowyml/stacks/plugins.py +115 -0
- flowyml/stacks/registry.py +2 -1
- flowyml/storage/sql.py +401 -12
- flowyml/tracking/experiment.py +8 -5
- flowyml/ui/backend/Dockerfile +87 -16
- flowyml/ui/backend/auth.py +12 -2
- flowyml/ui/backend/main.py +149 -5
- flowyml/ui/backend/routers/ai_context.py +226 -0
- flowyml/ui/backend/routers/assets.py +23 -4
- flowyml/ui/backend/routers/auth.py +96 -0
- flowyml/ui/backend/routers/deployments.py +660 -0
- flowyml/ui/backend/routers/model_explorer.py +597 -0
- flowyml/ui/backend/routers/plugins.py +103 -51
- flowyml/ui/backend/routers/projects.py +91 -8
- flowyml/ui/backend/routers/runs.py +20 -1
- flowyml/ui/backend/routers/schedules.py +22 -17
- flowyml/ui/backend/routers/templates.py +319 -0
- flowyml/ui/backend/routers/websocket.py +2 -2
- flowyml/ui/frontend/Dockerfile +55 -6
- flowyml/ui/frontend/dist/assets/index-B5AsPTSz.css +1 -0
- flowyml/ui/frontend/dist/assets/index-dFbZ8wD8.js +753 -0
- flowyml/ui/frontend/dist/index.html +2 -2
- flowyml/ui/frontend/dist/logo.png +0 -0
- flowyml/ui/frontend/nginx.conf +65 -4
- flowyml/ui/frontend/package-lock.json +1404 -74
- flowyml/ui/frontend/package.json +3 -0
- flowyml/ui/frontend/public/logo.png +0 -0
- flowyml/ui/frontend/src/App.jsx +10 -7
- flowyml/ui/frontend/src/app/auth/Login.jsx +90 -0
- flowyml/ui/frontend/src/app/dashboard/page.jsx +8 -8
- flowyml/ui/frontend/src/app/deployments/page.jsx +786 -0
- flowyml/ui/frontend/src/app/model-explorer/page.jsx +1031 -0
- flowyml/ui/frontend/src/app/pipelines/page.jsx +12 -2
- flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +19 -6
- flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +36 -24
- flowyml/ui/frontend/src/app/runs/page.jsx +8 -2
- flowyml/ui/frontend/src/app/settings/page.jsx +267 -253
- flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +29 -7
- flowyml/ui/frontend/src/components/Layout.jsx +6 -0
- flowyml/ui/frontend/src/components/PipelineGraph.jsx +79 -29
- flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +36 -6
- flowyml/ui/frontend/src/components/RunMetaPanel.jsx +113 -0
- flowyml/ui/frontend/src/components/ai/AIAssistantButton.jsx +71 -0
- flowyml/ui/frontend/src/components/ai/AIAssistantPanel.jsx +420 -0
- flowyml/ui/frontend/src/components/header/Header.jsx +22 -0
- flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +4 -4
- flowyml/ui/frontend/src/components/plugins/{ZenMLIntegration.jsx → StackImport.jsx} +38 -12
- flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +36 -13
- flowyml/ui/frontend/src/contexts/AIAssistantContext.jsx +245 -0
- flowyml/ui/frontend/src/contexts/AuthContext.jsx +108 -0
- flowyml/ui/frontend/src/hooks/useAIContext.js +156 -0
- flowyml/ui/frontend/src/hooks/useWebGPU.js +54 -0
- flowyml/ui/frontend/src/layouts/MainLayout.jsx +6 -0
- flowyml/ui/frontend/src/router/index.jsx +47 -20
- flowyml/ui/frontend/src/services/pluginService.js +3 -1
- flowyml/ui/server_manager.py +5 -5
- flowyml/ui/utils.py +157 -39
- flowyml/utils/config.py +37 -15
- flowyml/utils/model_introspection.py +123 -0
- flowyml/utils/observability.py +30 -0
- flowyml-1.8.0.dist-info/METADATA +174 -0
- {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/RECORD +123 -65
- {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/WHEEL +1 -1
- flowyml/ui/frontend/dist/assets/index-B40RsQDq.css +0 -1
- flowyml/ui/frontend/dist/assets/index-CjI0zKCn.js +0 -685
- flowyml-1.7.2.dist-info/METADATA +0 -477
- {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/entry_points.txt +0 -0
- {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/licenses/LICENSE +0 -0
flowyml/plugins/base.py
ADDED
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
"""FlowyML Plugin System - Base Classes.
|
|
2
|
+
|
|
3
|
+
This module defines the base classes for all FlowyML plugins, providing
|
|
4
|
+
a consistent interface for extending FlowyML with custom components.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from flowyml.plugins.base import ExperimentTracker, FeatureStore
|
|
8
|
+
|
|
9
|
+
class MyTracker(ExperimentTracker):
|
|
10
|
+
def log_params(self, params: dict):
|
|
11
|
+
...
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from typing import Any
|
|
18
|
+
import logging
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PluginType(Enum):
|
|
24
|
+
"""Types of plugins available in FlowyML."""
|
|
25
|
+
|
|
26
|
+
# Core MLOps Components
|
|
27
|
+
EXPERIMENT_TRACKER = "experiment_tracker"
|
|
28
|
+
ARTIFACT_STORE = "artifact_store"
|
|
29
|
+
ORCHESTRATOR = "orchestrator"
|
|
30
|
+
CONTAINER_REGISTRY = "container_registry"
|
|
31
|
+
MODEL_REGISTRY = "model_registry"
|
|
32
|
+
|
|
33
|
+
# Data Components
|
|
34
|
+
FEATURE_STORE = "feature_store"
|
|
35
|
+
DATA_VALIDATOR = "data_validator"
|
|
36
|
+
|
|
37
|
+
# Infrastructure
|
|
38
|
+
STEP_OPERATOR = "step_operator"
|
|
39
|
+
IMAGE_BUILDER = "image_builder"
|
|
40
|
+
|
|
41
|
+
# Notifications & Alerts
|
|
42
|
+
ALERTER = "alerter"
|
|
43
|
+
|
|
44
|
+
# Deployment
|
|
45
|
+
MODEL_DEPLOYER = "model_deployer"
|
|
46
|
+
|
|
47
|
+
# Other
|
|
48
|
+
CUSTOM = "custom"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class PluginMetadata:
|
|
53
|
+
"""Metadata for a plugin."""
|
|
54
|
+
|
|
55
|
+
name: str
|
|
56
|
+
description: str
|
|
57
|
+
plugin_type: PluginType
|
|
58
|
+
version: str = "1.0.0"
|
|
59
|
+
author: str = "FlowyML"
|
|
60
|
+
packages: list[str] = field(default_factory=list)
|
|
61
|
+
documentation_url: str = ""
|
|
62
|
+
tags: list[str] = field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BasePlugin(ABC):
|
|
66
|
+
"""Base class for all FlowyML plugins.
|
|
67
|
+
|
|
68
|
+
All plugins must inherit from this class and implement the required methods.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# Class-level metadata (override in subclasses)
|
|
72
|
+
METADATA: PluginMetadata | None = None
|
|
73
|
+
|
|
74
|
+
def __init__(self, name: str = None, **config):
|
|
75
|
+
"""Initialize the plugin.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
name: Optional name override for this instance.
|
|
79
|
+
**config: Configuration parameters for the plugin.
|
|
80
|
+
"""
|
|
81
|
+
self._name = name or self.__class__.__name__
|
|
82
|
+
self._config = config
|
|
83
|
+
self._is_initialized = False
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def name(self) -> str:
|
|
87
|
+
"""Get the plugin instance name."""
|
|
88
|
+
return self._name
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def config(self) -> dict[str, Any]:
|
|
92
|
+
"""Get the plugin configuration."""
|
|
93
|
+
return self._config
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
@abstractmethod
|
|
97
|
+
def plugin_type(self) -> PluginType:
|
|
98
|
+
"""Return the type of this plugin."""
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
def initialize(self) -> None:
|
|
102
|
+
"""Initialize the plugin. Called before first use.
|
|
103
|
+
|
|
104
|
+
Override this method to perform setup operations like
|
|
105
|
+
connecting to external services.
|
|
106
|
+
"""
|
|
107
|
+
self._is_initialized = True
|
|
108
|
+
|
|
109
|
+
def cleanup(self) -> None:
|
|
110
|
+
"""Cleanup resources used by the plugin.
|
|
111
|
+
|
|
112
|
+
Override this method to close connections, flush buffers, etc.
|
|
113
|
+
"""
|
|
114
|
+
self._is_initialized = False
|
|
115
|
+
|
|
116
|
+
def validate(self) -> bool:
|
|
117
|
+
"""Validate that the plugin is properly configured.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if the plugin is valid and ready to use.
|
|
121
|
+
"""
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
def to_dict(self) -> dict[str, Any]:
|
|
125
|
+
"""Serialize the plugin configuration to a dictionary.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Dictionary representation of the plugin.
|
|
129
|
+
"""
|
|
130
|
+
return {
|
|
131
|
+
"name": self._name,
|
|
132
|
+
"type": self.plugin_type.value,
|
|
133
|
+
"config": self._config,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def from_dict(cls, data: dict[str, Any]) -> "BasePlugin":
|
|
138
|
+
"""Create a plugin instance from a dictionary.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
data: Dictionary containing plugin configuration.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
A new plugin instance.
|
|
145
|
+
"""
|
|
146
|
+
config = data.get("config", {})
|
|
147
|
+
name = data.get("name")
|
|
148
|
+
return cls(name=name, **config)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ============================================================================
|
|
152
|
+
# EXPERIMENT TRACKERS
|
|
153
|
+
# ============================================================================
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ExperimentTracker(BasePlugin):
|
|
157
|
+
"""Base class for experiment tracking plugins.
|
|
158
|
+
|
|
159
|
+
Experiment trackers record parameters, metrics, and artifacts
|
|
160
|
+
from ML experiments for reproducibility and comparison.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def plugin_type(self) -> PluginType:
|
|
165
|
+
return PluginType.EXPERIMENT_TRACKER
|
|
166
|
+
|
|
167
|
+
@abstractmethod
|
|
168
|
+
def start_run(self, run_name: str, experiment_name: str = None, tags: dict = None) -> str:
|
|
169
|
+
"""Start a new experiment run.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
run_name: Name for this run.
|
|
173
|
+
experiment_name: Name of the experiment to log to.
|
|
174
|
+
tags: Optional tags for the run.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Run ID.
|
|
178
|
+
"""
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
@abstractmethod
|
|
182
|
+
def end_run(self, status: str = "FINISHED") -> None:
|
|
183
|
+
"""End the current run.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
status: Final status of the run (FINISHED, FAILED, etc.)
|
|
187
|
+
"""
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def log_params(self, params: dict[str, Any]) -> None:
|
|
192
|
+
"""Log parameters for the current run.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
params: Dictionary of parameter names and values.
|
|
196
|
+
"""
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
@abstractmethod
|
|
200
|
+
def log_metrics(self, metrics: dict[str, float], step: int = None) -> None:
|
|
201
|
+
"""Log metrics for the current run.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
metrics: Dictionary of metric names and values.
|
|
205
|
+
step: Optional step number for the metrics.
|
|
206
|
+
"""
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
def log_artifact(self, local_path: str, artifact_path: str = None) -> None:
|
|
210
|
+
"""Log an artifact (file) for the current run.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
local_path: Path to the local file to log.
|
|
214
|
+
artifact_path: Optional path within the artifact store.
|
|
215
|
+
"""
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
def log_model(self, model: Any, artifact_path: str, model_type: str = None) -> None:
|
|
219
|
+
"""Log a model for the current run.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
model: The model object to log.
|
|
223
|
+
artifact_path: Path within the artifact store.
|
|
224
|
+
model_type: Type of model (sklearn, pytorch, tensorflow, etc.)
|
|
225
|
+
"""
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
def get_tracking_uri(self) -> str:
|
|
229
|
+
"""Get the tracking URI for this tracker.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
The tracking URI.
|
|
233
|
+
"""
|
|
234
|
+
return self._config.get("tracking_uri", "")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# ============================================================================
|
|
238
|
+
# ARTIFACT STORES
|
|
239
|
+
# ============================================================================
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class ArtifactStorePlugin(BasePlugin):
|
|
243
|
+
"""Base class for artifact storage plugins.
|
|
244
|
+
|
|
245
|
+
Artifact stores handle the persistence of artifacts like
|
|
246
|
+
datasets, models, and other files.
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def plugin_type(self) -> PluginType:
|
|
251
|
+
return PluginType.ARTIFACT_STORE
|
|
252
|
+
|
|
253
|
+
@abstractmethod
|
|
254
|
+
def save(self, artifact: Any, path: str) -> str:
|
|
255
|
+
"""Save an artifact to the store.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
artifact: The artifact to save.
|
|
259
|
+
path: Path within the store.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Full URI of the saved artifact.
|
|
263
|
+
"""
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
@abstractmethod
|
|
267
|
+
def load(self, path: str) -> Any:
|
|
268
|
+
"""Load an artifact from the store.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
path: Path to the artifact.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
The loaded artifact.
|
|
275
|
+
"""
|
|
276
|
+
pass
|
|
277
|
+
|
|
278
|
+
@abstractmethod
|
|
279
|
+
def exists(self, path: str) -> bool:
|
|
280
|
+
"""Check if an artifact exists.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
path: Path to check.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
True if the artifact exists.
|
|
287
|
+
"""
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
def delete(self, path: str) -> bool:
|
|
291
|
+
"""Delete an artifact from the store.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
path: Path to delete.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
True if deletion was successful.
|
|
298
|
+
"""
|
|
299
|
+
return False
|
|
300
|
+
|
|
301
|
+
def list(self, path: str = "") -> list[str]: # noqa: A003
|
|
302
|
+
"""List artifacts in a directory.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
path: Directory path to list.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
List of artifact paths.
|
|
309
|
+
"""
|
|
310
|
+
return []
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def root_path(self) -> str:
|
|
314
|
+
"""Get the root path of the artifact store."""
|
|
315
|
+
return self._config.get("path", "")
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# ============================================================================
|
|
319
|
+
# ORCHESTRATORS
|
|
320
|
+
# ============================================================================
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class OrchestratorPlugin(BasePlugin):
|
|
324
|
+
"""Base class for orchestrator plugins.
|
|
325
|
+
|
|
326
|
+
Orchestrators manage the execution of pipeline steps,
|
|
327
|
+
handling scheduling, resource allocation, and monitoring.
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def plugin_type(self) -> PluginType:
|
|
332
|
+
return PluginType.ORCHESTRATOR
|
|
333
|
+
|
|
334
|
+
@abstractmethod
|
|
335
|
+
def run_pipeline(
|
|
336
|
+
self,
|
|
337
|
+
pipeline: Any,
|
|
338
|
+
run_id: str,
|
|
339
|
+
context: dict[str, Any] = None,
|
|
340
|
+
**kwargs,
|
|
341
|
+
) -> Any:
|
|
342
|
+
"""Run a pipeline.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
pipeline: The pipeline to run.
|
|
346
|
+
run_id: Unique identifier for this run.
|
|
347
|
+
context: Optional context dictionary.
|
|
348
|
+
**kwargs: Additional orchestrator-specific arguments.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Run result or status.
|
|
352
|
+
"""
|
|
353
|
+
pass
|
|
354
|
+
|
|
355
|
+
def get_run_status(self, run_id: str) -> str:
|
|
356
|
+
"""Get the status of a pipeline run.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
run_id: The run identifier.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Run status string.
|
|
363
|
+
"""
|
|
364
|
+
return "unknown"
|
|
365
|
+
|
|
366
|
+
def cancel_run(self, run_id: str) -> bool:
|
|
367
|
+
"""Cancel a running pipeline.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
run_id: The run identifier.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
True if cancellation was successful.
|
|
374
|
+
"""
|
|
375
|
+
return False
|
|
376
|
+
|
|
377
|
+
def list_runs(self, pipeline_name: str = None, limit: int = 100) -> list[dict]:
|
|
378
|
+
"""List pipeline runs.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
pipeline_name: Optional filter by pipeline name.
|
|
382
|
+
limit: Maximum number of runs to return.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
List of run dictionaries.
|
|
386
|
+
"""
|
|
387
|
+
return []
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
# ============================================================================
|
|
391
|
+
# CONTAINER REGISTRIES
|
|
392
|
+
# ============================================================================
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class ContainerRegistryPlugin(BasePlugin):
|
|
396
|
+
"""Base class for container registry plugins.
|
|
397
|
+
|
|
398
|
+
Container registries store and manage Docker images
|
|
399
|
+
for pipeline execution.
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
@property
|
|
403
|
+
def plugin_type(self) -> PluginType:
|
|
404
|
+
return PluginType.CONTAINER_REGISTRY
|
|
405
|
+
|
|
406
|
+
@abstractmethod
|
|
407
|
+
def push_image(self, image_name: str, tag: str = "latest") -> str:
|
|
408
|
+
"""Push an image to the registry.
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
image_name: Name of the image.
|
|
412
|
+
tag: Image tag.
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Full image URI.
|
|
416
|
+
"""
|
|
417
|
+
pass
|
|
418
|
+
|
|
419
|
+
def pull_image(self, image_name: str, tag: str = "latest") -> None:
|
|
420
|
+
"""Pull an image from the registry.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
image_name: Name of the image.
|
|
424
|
+
tag: Image tag.
|
|
425
|
+
"""
|
|
426
|
+
pass
|
|
427
|
+
|
|
428
|
+
@abstractmethod
|
|
429
|
+
def get_image_uri(self, image_name: str, tag: str = "latest") -> str:
|
|
430
|
+
"""Get the full URI for an image.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
image_name: Name of the image.
|
|
434
|
+
tag: Image tag.
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Full image URI.
|
|
438
|
+
"""
|
|
439
|
+
pass
|
|
440
|
+
|
|
441
|
+
@property
|
|
442
|
+
def registry_uri(self) -> str:
|
|
443
|
+
"""Get the registry URI."""
|
|
444
|
+
return self._config.get("uri", "")
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
# ============================================================================
|
|
448
|
+
# FEATURE STORES
|
|
449
|
+
# ============================================================================
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class FeatureStorePlugin(BasePlugin):
|
|
453
|
+
"""Base class for feature store plugins.
|
|
454
|
+
|
|
455
|
+
Feature stores manage ML features for training and inference,
|
|
456
|
+
providing versioning, serving, and discovery capabilities.
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
@property
|
|
460
|
+
def plugin_type(self) -> PluginType:
|
|
461
|
+
return PluginType.FEATURE_STORE
|
|
462
|
+
|
|
463
|
+
@abstractmethod
|
|
464
|
+
def get_feature_view(self, name: str, version: str = None) -> Any:
|
|
465
|
+
"""Get a feature view by name.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
name: Name of the feature view.
|
|
469
|
+
version: Optional version.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
The feature view.
|
|
473
|
+
"""
|
|
474
|
+
pass
|
|
475
|
+
|
|
476
|
+
@abstractmethod
|
|
477
|
+
def get_online_features(
|
|
478
|
+
self,
|
|
479
|
+
feature_refs: list[str],
|
|
480
|
+
entity_rows: list[dict],
|
|
481
|
+
) -> dict[str, list]:
|
|
482
|
+
"""Get online (real-time) features.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
feature_refs: List of feature references (feature_view:feature).
|
|
486
|
+
entity_rows: Entity key-value pairs.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
Dictionary of feature names to value lists.
|
|
490
|
+
"""
|
|
491
|
+
pass
|
|
492
|
+
|
|
493
|
+
def get_historical_features(
|
|
494
|
+
self,
|
|
495
|
+
feature_refs: list[str],
|
|
496
|
+
entity_df: Any,
|
|
497
|
+
) -> Any:
|
|
498
|
+
"""Get historical features for training.
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
feature_refs: List of feature references.
|
|
502
|
+
entity_df: DataFrame with entity keys and timestamps.
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
DataFrame with features.
|
|
506
|
+
"""
|
|
507
|
+
pass
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
# ============================================================================
|
|
511
|
+
# DATA VALIDATORS
|
|
512
|
+
# ============================================================================
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
class DataValidatorPlugin(BasePlugin):
|
|
516
|
+
"""Base class for data validation plugins.
|
|
517
|
+
|
|
518
|
+
Data validators check data quality, schema conformance,
|
|
519
|
+
and detect anomalies in datasets.
|
|
520
|
+
"""
|
|
521
|
+
|
|
522
|
+
@property
|
|
523
|
+
def plugin_type(self) -> PluginType:
|
|
524
|
+
return PluginType.DATA_VALIDATOR
|
|
525
|
+
|
|
526
|
+
@abstractmethod
|
|
527
|
+
def validate(self, data: Any, expectations: Any = None) -> dict[str, Any]:
|
|
528
|
+
"""Validate data against expectations.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
data: The data to validate.
|
|
532
|
+
expectations: Validation rules/expectations.
|
|
533
|
+
|
|
534
|
+
Returns:
|
|
535
|
+
Validation results dictionary.
|
|
536
|
+
"""
|
|
537
|
+
pass
|
|
538
|
+
|
|
539
|
+
def get_data_profile(self, data: Any) -> dict[str, Any]:
|
|
540
|
+
"""Generate a profile of the data.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
data: The data to profile.
|
|
544
|
+
|
|
545
|
+
Returns:
|
|
546
|
+
Data profile dictionary.
|
|
547
|
+
"""
|
|
548
|
+
return {}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# ============================================================================
|
|
552
|
+
# MODEL REGISTRY
|
|
553
|
+
# ============================================================================
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class ModelRegistryPlugin(BasePlugin):
|
|
557
|
+
"""Base class for model registry plugins.
|
|
558
|
+
|
|
559
|
+
Model registries track, version, and manage ML models
|
|
560
|
+
throughout their lifecycle.
|
|
561
|
+
"""
|
|
562
|
+
|
|
563
|
+
@property
|
|
564
|
+
def plugin_type(self) -> PluginType:
|
|
565
|
+
return PluginType.MODEL_REGISTRY
|
|
566
|
+
|
|
567
|
+
@abstractmethod
|
|
568
|
+
def register_model(
|
|
569
|
+
self,
|
|
570
|
+
name: str,
|
|
571
|
+
model_uri: str,
|
|
572
|
+
version: str = None,
|
|
573
|
+
metadata: dict = None,
|
|
574
|
+
) -> str:
|
|
575
|
+
"""Register a model.
|
|
576
|
+
|
|
577
|
+
Args:
|
|
578
|
+
name: Model name.
|
|
579
|
+
model_uri: URI to the model artifact.
|
|
580
|
+
version: Optional version string.
|
|
581
|
+
metadata: Optional metadata dictionary.
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
Model version identifier.
|
|
585
|
+
"""
|
|
586
|
+
pass
|
|
587
|
+
|
|
588
|
+
@abstractmethod
|
|
589
|
+
def get_model(self, name: str, version: str = None) -> Any:
|
|
590
|
+
"""Get a model by name.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
name: Model name.
|
|
594
|
+
version: Optional version (defaults to latest).
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
The model.
|
|
598
|
+
"""
|
|
599
|
+
pass
|
|
600
|
+
|
|
601
|
+
def list_models(self, name: str = None) -> list[dict]:
|
|
602
|
+
"""List registered models.
|
|
603
|
+
|
|
604
|
+
Args:
|
|
605
|
+
name: Optional filter by model name.
|
|
606
|
+
|
|
607
|
+
Returns:
|
|
608
|
+
List of model metadata dictionaries.
|
|
609
|
+
"""
|
|
610
|
+
return []
|
|
611
|
+
|
|
612
|
+
def transition_model_stage(
|
|
613
|
+
self,
|
|
614
|
+
name: str,
|
|
615
|
+
version: str,
|
|
616
|
+
stage: str,
|
|
617
|
+
) -> None:
|
|
618
|
+
"""Transition a model to a new stage.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
name: Model name.
|
|
622
|
+
version: Model version.
|
|
623
|
+
stage: Target stage (staging, production, archived).
|
|
624
|
+
"""
|
|
625
|
+
pass
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
# ============================================================================
|
|
629
|
+
# ALERTERS
|
|
630
|
+
# ============================================================================
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
class AlerterPlugin(BasePlugin):
|
|
634
|
+
"""Base class for alerter plugins.
|
|
635
|
+
|
|
636
|
+
Alerters send notifications about pipeline events,
|
|
637
|
+
errors, and other important occurrences.
|
|
638
|
+
"""
|
|
639
|
+
|
|
640
|
+
@property
|
|
641
|
+
def plugin_type(self) -> PluginType:
|
|
642
|
+
return PluginType.ALERTER
|
|
643
|
+
|
|
644
|
+
@abstractmethod
|
|
645
|
+
def send_alert(
|
|
646
|
+
self,
|
|
647
|
+
title: str,
|
|
648
|
+
message: str,
|
|
649
|
+
level: str = "info",
|
|
650
|
+
**kwargs,
|
|
651
|
+
) -> bool:
|
|
652
|
+
"""Send an alert notification.
|
|
653
|
+
|
|
654
|
+
Args:
|
|
655
|
+
title: Alert title.
|
|
656
|
+
message: Alert message body.
|
|
657
|
+
level: Alert level (info, warning, error, critical).
|
|
658
|
+
**kwargs: Additional alerter-specific parameters.
|
|
659
|
+
|
|
660
|
+
Returns:
|
|
661
|
+
True if alert was sent successfully.
|
|
662
|
+
"""
|
|
663
|
+
pass
|
|
664
|
+
|
|
665
|
+
def send_success(self, title: str, message: str) -> bool:
|
|
666
|
+
"""Send a success notification."""
|
|
667
|
+
return self.send_alert(title, message, level="success")
|
|
668
|
+
|
|
669
|
+
def send_error(self, title: str, message: str) -> bool:
|
|
670
|
+
"""Send an error notification."""
|
|
671
|
+
return self.send_alert(title, message, level="error")
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
# ============================================================================
|
|
675
|
+
# MODEL DEPLOYER
|
|
676
|
+
# ============================================================================
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
class ModelDeployerPlugin(BasePlugin):
|
|
680
|
+
"""Base class for model deployment plugins.
|
|
681
|
+
|
|
682
|
+
Model deployers handle deploying ML models to inference endpoints
|
|
683
|
+
for serving predictions.
|
|
684
|
+
"""
|
|
685
|
+
|
|
686
|
+
@property
|
|
687
|
+
def plugin_type(self) -> PluginType:
|
|
688
|
+
return PluginType.MODEL_DEPLOYER
|
|
689
|
+
|
|
690
|
+
@abstractmethod
|
|
691
|
+
def deploy(
|
|
692
|
+
self,
|
|
693
|
+
model: Any,
|
|
694
|
+
endpoint_name: str,
|
|
695
|
+
model_name: str = None,
|
|
696
|
+
**config,
|
|
697
|
+
) -> str:
|
|
698
|
+
"""Deploy a model to an endpoint.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
model: The model to deploy (URI, artifact, or object).
|
|
702
|
+
endpoint_name: Name for the endpoint.
|
|
703
|
+
model_name: Optional model name in registry.
|
|
704
|
+
**config: Deployment configuration.
|
|
705
|
+
|
|
706
|
+
Returns:
|
|
707
|
+
Endpoint URI or identifier.
|
|
708
|
+
"""
|
|
709
|
+
pass
|
|
710
|
+
|
|
711
|
+
@abstractmethod
|
|
712
|
+
def predict(self, endpoint: str, data: Any) -> Any:
|
|
713
|
+
"""Make predictions using a deployed model.
|
|
714
|
+
|
|
715
|
+
Args:
|
|
716
|
+
endpoint: Endpoint URI or identifier.
|
|
717
|
+
data: Input data for prediction.
|
|
718
|
+
|
|
719
|
+
Returns:
|
|
720
|
+
Prediction results.
|
|
721
|
+
"""
|
|
722
|
+
pass
|
|
723
|
+
|
|
724
|
+
def undeploy(self, endpoint: str) -> bool:
|
|
725
|
+
"""Undeploy a model endpoint.
|
|
726
|
+
|
|
727
|
+
Args:
|
|
728
|
+
endpoint: Endpoint URI or identifier.
|
|
729
|
+
|
|
730
|
+
Returns:
|
|
731
|
+
True if successful.
|
|
732
|
+
"""
|
|
733
|
+
return False
|
|
734
|
+
|
|
735
|
+
def list_endpoints(self) -> list[dict]:
|
|
736
|
+
"""List all deployed endpoints.
|
|
737
|
+
|
|
738
|
+
Returns:
|
|
739
|
+
List of endpoint metadata dictionaries.
|
|
740
|
+
"""
|
|
741
|
+
return []
|
|
742
|
+
|
|
743
|
+
def get_endpoint_status(self, endpoint: str) -> dict[str, Any]:
|
|
744
|
+
"""Get the status of an endpoint.
|
|
745
|
+
|
|
746
|
+
Args:
|
|
747
|
+
endpoint: Endpoint URI or identifier.
|
|
748
|
+
|
|
749
|
+
Returns:
|
|
750
|
+
Status dictionary.
|
|
751
|
+
"""
|
|
752
|
+
return {"status": "unknown"}
|