flowyml 1.7.1__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.
Files changed (137) hide show
  1. flowyml/assets/base.py +15 -0
  2. flowyml/assets/dataset.py +570 -17
  3. flowyml/assets/metrics.py +5 -0
  4. flowyml/assets/model.py +1052 -15
  5. flowyml/cli/main.py +709 -0
  6. flowyml/cli/stack_cli.py +138 -25
  7. flowyml/core/__init__.py +17 -0
  8. flowyml/core/executor.py +231 -37
  9. flowyml/core/image_builder.py +129 -0
  10. flowyml/core/log_streamer.py +227 -0
  11. flowyml/core/orchestrator.py +59 -4
  12. flowyml/core/pipeline.py +65 -13
  13. flowyml/core/routing.py +558 -0
  14. flowyml/core/scheduler.py +88 -5
  15. flowyml/core/step.py +9 -1
  16. flowyml/core/step_grouping.py +49 -35
  17. flowyml/core/types.py +407 -0
  18. flowyml/integrations/keras.py +247 -82
  19. flowyml/monitoring/alerts.py +10 -0
  20. flowyml/monitoring/notifications.py +104 -25
  21. flowyml/monitoring/slack_blocks.py +323 -0
  22. flowyml/plugins/__init__.py +251 -0
  23. flowyml/plugins/alerters/__init__.py +1 -0
  24. flowyml/plugins/alerters/slack.py +168 -0
  25. flowyml/plugins/base.py +752 -0
  26. flowyml/plugins/config.py +478 -0
  27. flowyml/plugins/deployers/__init__.py +22 -0
  28. flowyml/plugins/deployers/gcp_cloud_run.py +200 -0
  29. flowyml/plugins/deployers/sagemaker.py +306 -0
  30. flowyml/plugins/deployers/vertex.py +290 -0
  31. flowyml/plugins/integration.py +369 -0
  32. flowyml/plugins/manager.py +510 -0
  33. flowyml/plugins/model_registries/__init__.py +22 -0
  34. flowyml/plugins/model_registries/mlflow.py +159 -0
  35. flowyml/plugins/model_registries/sagemaker.py +489 -0
  36. flowyml/plugins/model_registries/vertex.py +386 -0
  37. flowyml/plugins/orchestrators/__init__.py +13 -0
  38. flowyml/plugins/orchestrators/sagemaker.py +443 -0
  39. flowyml/plugins/orchestrators/vertex_ai.py +461 -0
  40. flowyml/plugins/registries/__init__.py +13 -0
  41. flowyml/plugins/registries/ecr.py +321 -0
  42. flowyml/plugins/registries/gcr.py +313 -0
  43. flowyml/plugins/registry.py +454 -0
  44. flowyml/plugins/stack.py +494 -0
  45. flowyml/plugins/stack_config.py +537 -0
  46. flowyml/plugins/stores/__init__.py +13 -0
  47. flowyml/plugins/stores/gcs.py +460 -0
  48. flowyml/plugins/stores/s3.py +453 -0
  49. flowyml/plugins/trackers/__init__.py +11 -0
  50. flowyml/plugins/trackers/mlflow.py +316 -0
  51. flowyml/plugins/validators/__init__.py +3 -0
  52. flowyml/plugins/validators/deepchecks.py +119 -0
  53. flowyml/registry/__init__.py +2 -1
  54. flowyml/registry/model_environment.py +109 -0
  55. flowyml/registry/model_registry.py +241 -96
  56. flowyml/serving/__init__.py +17 -0
  57. flowyml/serving/model_server.py +628 -0
  58. flowyml/stacks/__init__.py +60 -0
  59. flowyml/stacks/aws.py +93 -0
  60. flowyml/stacks/base.py +62 -0
  61. flowyml/stacks/components.py +12 -0
  62. flowyml/stacks/gcp.py +44 -9
  63. flowyml/stacks/plugins.py +115 -0
  64. flowyml/stacks/registry.py +2 -1
  65. flowyml/storage/sql.py +401 -12
  66. flowyml/tracking/experiment.py +8 -5
  67. flowyml/ui/backend/Dockerfile +87 -16
  68. flowyml/ui/backend/auth.py +12 -2
  69. flowyml/ui/backend/main.py +149 -5
  70. flowyml/ui/backend/routers/ai_context.py +226 -0
  71. flowyml/ui/backend/routers/assets.py +23 -4
  72. flowyml/ui/backend/routers/auth.py +96 -0
  73. flowyml/ui/backend/routers/deployments.py +660 -0
  74. flowyml/ui/backend/routers/model_explorer.py +597 -0
  75. flowyml/ui/backend/routers/plugins.py +103 -51
  76. flowyml/ui/backend/routers/projects.py +91 -8
  77. flowyml/ui/backend/routers/runs.py +132 -1
  78. flowyml/ui/backend/routers/schedules.py +54 -29
  79. flowyml/ui/backend/routers/templates.py +319 -0
  80. flowyml/ui/backend/routers/websocket.py +2 -2
  81. flowyml/ui/frontend/Dockerfile +55 -6
  82. flowyml/ui/frontend/dist/assets/index-B5AsPTSz.css +1 -0
  83. flowyml/ui/frontend/dist/assets/index-dFbZ8wD8.js +753 -0
  84. flowyml/ui/frontend/dist/index.html +2 -2
  85. flowyml/ui/frontend/dist/logo.png +0 -0
  86. flowyml/ui/frontend/nginx.conf +65 -4
  87. flowyml/ui/frontend/package-lock.json +1415 -74
  88. flowyml/ui/frontend/package.json +4 -0
  89. flowyml/ui/frontend/public/logo.png +0 -0
  90. flowyml/ui/frontend/src/App.jsx +10 -7
  91. flowyml/ui/frontend/src/app/assets/page.jsx +890 -321
  92. flowyml/ui/frontend/src/app/auth/Login.jsx +90 -0
  93. flowyml/ui/frontend/src/app/dashboard/page.jsx +8 -8
  94. flowyml/ui/frontend/src/app/deployments/page.jsx +786 -0
  95. flowyml/ui/frontend/src/app/model-explorer/page.jsx +1031 -0
  96. flowyml/ui/frontend/src/app/pipelines/page.jsx +12 -2
  97. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +19 -6
  98. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectMetricsPanel.jsx +1 -1
  99. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +601 -101
  100. flowyml/ui/frontend/src/app/runs/page.jsx +8 -2
  101. flowyml/ui/frontend/src/app/settings/page.jsx +267 -253
  102. flowyml/ui/frontend/src/components/ArtifactViewer.jsx +62 -2
  103. flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +424 -29
  104. flowyml/ui/frontend/src/components/AssetTreeHierarchy.jsx +119 -11
  105. flowyml/ui/frontend/src/components/DatasetViewer.jsx +753 -0
  106. flowyml/ui/frontend/src/components/Layout.jsx +6 -0
  107. flowyml/ui/frontend/src/components/PipelineGraph.jsx +79 -29
  108. flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +36 -6
  109. flowyml/ui/frontend/src/components/RunMetaPanel.jsx +113 -0
  110. flowyml/ui/frontend/src/components/TrainingHistoryChart.jsx +514 -0
  111. flowyml/ui/frontend/src/components/TrainingMetricsPanel.jsx +175 -0
  112. flowyml/ui/frontend/src/components/ai/AIAssistantButton.jsx +71 -0
  113. flowyml/ui/frontend/src/components/ai/AIAssistantPanel.jsx +420 -0
  114. flowyml/ui/frontend/src/components/header/Header.jsx +22 -0
  115. flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +4 -4
  116. flowyml/ui/frontend/src/components/plugins/{ZenMLIntegration.jsx → StackImport.jsx} +38 -12
  117. flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +36 -13
  118. flowyml/ui/frontend/src/contexts/AIAssistantContext.jsx +245 -0
  119. flowyml/ui/frontend/src/contexts/AuthContext.jsx +108 -0
  120. flowyml/ui/frontend/src/hooks/useAIContext.js +156 -0
  121. flowyml/ui/frontend/src/hooks/useWebGPU.js +54 -0
  122. flowyml/ui/frontend/src/layouts/MainLayout.jsx +6 -0
  123. flowyml/ui/frontend/src/router/index.jsx +47 -20
  124. flowyml/ui/frontend/src/services/pluginService.js +3 -1
  125. flowyml/ui/server_manager.py +5 -5
  126. flowyml/ui/utils.py +157 -39
  127. flowyml/utils/config.py +37 -15
  128. flowyml/utils/model_introspection.py +123 -0
  129. flowyml/utils/observability.py +30 -0
  130. flowyml-1.8.0.dist-info/METADATA +174 -0
  131. {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/RECORD +134 -73
  132. {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/WHEEL +1 -1
  133. flowyml/ui/frontend/dist/assets/index-BqDQvp63.js +0 -630
  134. flowyml/ui/frontend/dist/assets/index-By4trVyv.css +0 -1
  135. flowyml-1.7.1.dist-info/METADATA +0 -477
  136. {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/entry_points.txt +0 -0
  137. {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -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"}