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.
- flowyml/assets/base.py +15 -0
- flowyml/assets/dataset.py +570 -17
- flowyml/assets/metrics.py +5 -0
- flowyml/assets/model.py +1052 -15
- flowyml/cli/main.py +709 -0
- flowyml/cli/stack_cli.py +138 -25
- flowyml/core/__init__.py +17 -0
- flowyml/core/executor.py +231 -37
- flowyml/core/image_builder.py +129 -0
- flowyml/core/log_streamer.py +227 -0
- flowyml/core/orchestrator.py +59 -4
- flowyml/core/pipeline.py +65 -13
- flowyml/core/routing.py +558 -0
- flowyml/core/scheduler.py +88 -5
- flowyml/core/step.py +9 -1
- flowyml/core/step_grouping.py +49 -35
- flowyml/core/types.py +407 -0
- flowyml/integrations/keras.py +247 -82
- 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 +132 -1
- flowyml/ui/backend/routers/schedules.py +54 -29
- 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 +1415 -74
- flowyml/ui/frontend/package.json +4 -0
- flowyml/ui/frontend/public/logo.png +0 -0
- flowyml/ui/frontend/src/App.jsx +10 -7
- flowyml/ui/frontend/src/app/assets/page.jsx +890 -321
- 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/projects/[projectId]/_components/ProjectMetricsPanel.jsx +1 -1
- flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +601 -101
- 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/ArtifactViewer.jsx +62 -2
- flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +424 -29
- flowyml/ui/frontend/src/components/AssetTreeHierarchy.jsx +119 -11
- flowyml/ui/frontend/src/components/DatasetViewer.jsx +753 -0
- 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/TrainingHistoryChart.jsx +514 -0
- flowyml/ui/frontend/src/components/TrainingMetricsPanel.jsx +175 -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.1.dist-info → flowyml-1.8.0.dist-info}/RECORD +134 -73
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/WHEEL +1 -1
- flowyml/ui/frontend/dist/assets/index-BqDQvp63.js +0 -630
- flowyml/ui/frontend/dist/assets/index-By4trVyv.css +0 -1
- flowyml-1.7.1.dist-info/METADATA +0 -477
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/entry_points.txt +0 -0
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/licenses/LICENSE +0 -0
flowyml/plugins/stack.py
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"""FlowyML Stack - Unified Interface for ML Operations.
|
|
2
|
+
|
|
3
|
+
This module provides a unified, intuitive API for common ML operations.
|
|
4
|
+
Just configure your stack in flowyml.yaml and use these functions -
|
|
5
|
+
FlowyML automatically routes to the configured plugins.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
# flowyml.yaml
|
|
9
|
+
plugins:
|
|
10
|
+
experiment_tracker:
|
|
11
|
+
type: mlflow
|
|
12
|
+
tracking_uri: http://localhost:5000
|
|
13
|
+
artifact_store:
|
|
14
|
+
type: gcs
|
|
15
|
+
bucket: my-ml-artifacts
|
|
16
|
+
container_registry:
|
|
17
|
+
type: gcr
|
|
18
|
+
project: my-gcp-project
|
|
19
|
+
|
|
20
|
+
# In code - intuitive API, no plugin knowledge needed
|
|
21
|
+
from flowyml.plugins.stack import (
|
|
22
|
+
start_run, end_run,
|
|
23
|
+
log_params, log_metrics,
|
|
24
|
+
save_artifact, load_artifact,
|
|
25
|
+
save_model, load_model,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Just use - FlowyML routes to your configured plugins
|
|
29
|
+
start_run("training_v1")
|
|
30
|
+
log_params({"lr": 0.001, "epochs": 100})
|
|
31
|
+
|
|
32
|
+
# Train model...
|
|
33
|
+
log_metrics({"accuracy": 0.95})
|
|
34
|
+
save_artifact(test_data, "data/test.pkl")
|
|
35
|
+
save_model(model, "models/classifier")
|
|
36
|
+
|
|
37
|
+
end_run()
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
import logging
|
|
41
|
+
from typing import Any
|
|
42
|
+
from contextlib import contextmanager
|
|
43
|
+
|
|
44
|
+
from flowyml.plugins.config import (
|
|
45
|
+
get_config,
|
|
46
|
+
get_tracker,
|
|
47
|
+
get_artifact_store,
|
|
48
|
+
get_container_registry,
|
|
49
|
+
get_orchestrator,
|
|
50
|
+
get_alerter,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# =============================================================================
|
|
57
|
+
# EXPERIMENT TRACKING (Auto-routes to configured tracker)
|
|
58
|
+
# =============================================================================
|
|
59
|
+
|
|
60
|
+
_current_run_id: str | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def start_run(
|
|
64
|
+
run_name: str,
|
|
65
|
+
experiment_name: str = None,
|
|
66
|
+
tags: dict = None,
|
|
67
|
+
) -> str | None:
|
|
68
|
+
"""Start a new experiment run.
|
|
69
|
+
|
|
70
|
+
Uses the experiment_tracker configured in flowyml.yaml.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
run_name: Name for this run.
|
|
74
|
+
experiment_name: Optional experiment name.
|
|
75
|
+
tags: Optional tags for the run.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Run ID if successful, None otherwise.
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
start_run("training_v1", experiment_name="image_classification")
|
|
82
|
+
"""
|
|
83
|
+
global _current_run_id
|
|
84
|
+
tracker = get_tracker()
|
|
85
|
+
|
|
86
|
+
if tracker:
|
|
87
|
+
_current_run_id = tracker.start_run(run_name, experiment_name, tags)
|
|
88
|
+
return _current_run_id
|
|
89
|
+
else:
|
|
90
|
+
logger.warning("No experiment_tracker configured in flowyml.yaml")
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def end_run(status: str = "FINISHED") -> None:
|
|
95
|
+
"""End the current experiment run.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
status: Final status (FINISHED, FAILED, etc.)
|
|
99
|
+
"""
|
|
100
|
+
global _current_run_id
|
|
101
|
+
tracker = get_tracker()
|
|
102
|
+
|
|
103
|
+
if tracker:
|
|
104
|
+
tracker.end_run(status)
|
|
105
|
+
_current_run_id = None
|
|
106
|
+
else:
|
|
107
|
+
logger.warning("No experiment_tracker configured")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@contextmanager
|
|
111
|
+
def run(run_name: str, experiment_name: str = None, tags: dict = None):
|
|
112
|
+
"""Context manager for experiment runs.
|
|
113
|
+
|
|
114
|
+
Usage:
|
|
115
|
+
with run("my_training"):
|
|
116
|
+
log_params({"lr": 0.001})
|
|
117
|
+
# Train model...
|
|
118
|
+
log_metrics({"accuracy": 0.95})
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
start_run(run_name, experiment_name, tags)
|
|
122
|
+
yield
|
|
123
|
+
end_run("FINISHED")
|
|
124
|
+
except Exception:
|
|
125
|
+
end_run("FAILED")
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def log_params(params: dict[str, Any]) -> None:
|
|
130
|
+
"""Log parameters to the current run.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
params: Dictionary of parameter names and values.
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
log_params({"learning_rate": 0.001, "batch_size": 32})
|
|
137
|
+
"""
|
|
138
|
+
tracker = get_tracker()
|
|
139
|
+
if tracker:
|
|
140
|
+
tracker.log_params(params)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def log_metrics(metrics: dict[str, float], step: int = None) -> None:
|
|
144
|
+
"""Log metrics to the current run.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
metrics: Dictionary of metric names and values.
|
|
148
|
+
step: Optional step number.
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
log_metrics({"accuracy": 0.95, "loss": 0.05}, step=100)
|
|
152
|
+
"""
|
|
153
|
+
tracker = get_tracker()
|
|
154
|
+
if tracker:
|
|
155
|
+
tracker.log_metrics(metrics, step)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def log_artifact(local_path: str, artifact_path: str = None) -> None:
|
|
159
|
+
"""Log an artifact file to the current run.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
local_path: Path to the local file.
|
|
163
|
+
artifact_path: Optional subdirectory in artifacts.
|
|
164
|
+
"""
|
|
165
|
+
tracker = get_tracker()
|
|
166
|
+
if tracker and hasattr(tracker, "log_artifact"):
|
|
167
|
+
tracker.log_artifact(local_path, artifact_path)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def set_tag(key: str, value: str) -> None:
|
|
171
|
+
"""Set a tag on the current run."""
|
|
172
|
+
tracker = get_tracker()
|
|
173
|
+
if tracker and hasattr(tracker, "set_tag"):
|
|
174
|
+
tracker.set_tag(key, value)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def set_tags(tags: dict[str, str]) -> None:
|
|
178
|
+
"""Set multiple tags on the current run."""
|
|
179
|
+
tracker = get_tracker()
|
|
180
|
+
if tracker and hasattr(tracker, "set_tags"):
|
|
181
|
+
tracker.set_tags(tags)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# =============================================================================
|
|
185
|
+
# ARTIFACT STORAGE (Auto-routes to configured store)
|
|
186
|
+
# =============================================================================
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def save_artifact(artifact: Any, path: str) -> str | None:
|
|
190
|
+
"""Save an artifact to the configured artifact store.
|
|
191
|
+
|
|
192
|
+
Uses the artifact_store configured in flowyml.yaml.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
artifact: The artifact to save (model, data, etc.)
|
|
196
|
+
path: Path within the store.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Full URI of the saved artifact.
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
save_artifact(processed_data, "data/processed.pkl")
|
|
203
|
+
save_artifact({"config": config}, "configs/training.json")
|
|
204
|
+
"""
|
|
205
|
+
store = get_artifact_store()
|
|
206
|
+
|
|
207
|
+
if store:
|
|
208
|
+
return store.save(artifact, path)
|
|
209
|
+
else:
|
|
210
|
+
logger.warning("No artifact_store configured in flowyml.yaml")
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def load_artifact(path: str) -> Any | None:
|
|
215
|
+
"""Load an artifact from the configured artifact store.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
path: Path to the artifact.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
The loaded artifact.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
data = load_artifact("data/processed.pkl")
|
|
225
|
+
"""
|
|
226
|
+
store = get_artifact_store()
|
|
227
|
+
|
|
228
|
+
if store:
|
|
229
|
+
return store.load(path)
|
|
230
|
+
else:
|
|
231
|
+
logger.warning("No artifact_store configured")
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def artifact_exists(path: str) -> bool:
|
|
236
|
+
"""Check if an artifact exists in the store.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
path: Path to check.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
True if the artifact exists.
|
|
243
|
+
"""
|
|
244
|
+
store = get_artifact_store()
|
|
245
|
+
return store.exists(path) if store else False
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def list_artifacts(path: str = "") -> list[str]:
|
|
249
|
+
"""List artifacts in a directory.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
path: Directory path to list.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
List of artifact paths.
|
|
256
|
+
"""
|
|
257
|
+
store = get_artifact_store()
|
|
258
|
+
return store.list(path) if store else []
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def delete_artifact(path: str) -> bool:
|
|
262
|
+
"""Delete an artifact from the store.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
path: Path to delete.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
True if deletion was successful.
|
|
269
|
+
"""
|
|
270
|
+
store = get_artifact_store()
|
|
271
|
+
return store.delete(path) if store else False
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# =============================================================================
|
|
275
|
+
# MODEL MANAGEMENT (Uses both artifact store and tracker)
|
|
276
|
+
# =============================================================================
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def save_model(
|
|
280
|
+
model: Any,
|
|
281
|
+
path: str,
|
|
282
|
+
model_type: str = None,
|
|
283
|
+
register: bool = True,
|
|
284
|
+
model_name: str = None,
|
|
285
|
+
) -> str | None:
|
|
286
|
+
"""Save a model to the artifact store with tracking.
|
|
287
|
+
|
|
288
|
+
This is the recommended way to save models - it automatically:
|
|
289
|
+
1. Saves the model to the artifact store
|
|
290
|
+
2. Logs it to the experiment tracker (if in a run)
|
|
291
|
+
3. Optionally registers in model registry
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
model: The model object.
|
|
295
|
+
path: Path within the artifact store.
|
|
296
|
+
model_type: Type of model (sklearn, pytorch, tensorflow, etc.)
|
|
297
|
+
register: If True, register in model registry (if configured).
|
|
298
|
+
model_name: Name for model registry (uses path if not provided).
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Full URI of the saved model.
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
# Simple save
|
|
305
|
+
save_model(clf, "models/classifier")
|
|
306
|
+
|
|
307
|
+
# With explicit type
|
|
308
|
+
save_model(model, "models/neural_net", model_type="pytorch")
|
|
309
|
+
"""
|
|
310
|
+
# Save to artifact store
|
|
311
|
+
store = get_artifact_store()
|
|
312
|
+
uri = None
|
|
313
|
+
|
|
314
|
+
if store:
|
|
315
|
+
uri = store.save(model, path)
|
|
316
|
+
logger.info(f"Model saved to: {uri}")
|
|
317
|
+
|
|
318
|
+
# Log to tracker if in a run
|
|
319
|
+
tracker = get_tracker()
|
|
320
|
+
if tracker and _current_run_id:
|
|
321
|
+
if hasattr(tracker, "log_model"):
|
|
322
|
+
tracker.log_model(model, path, model_type=model_type)
|
|
323
|
+
if uri:
|
|
324
|
+
set_tag("model_uri", uri)
|
|
325
|
+
|
|
326
|
+
return uri
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def load_model(path: str) -> Any | None:
|
|
330
|
+
"""Load a model from the artifact store.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
path: Path to the model.
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
The loaded model.
|
|
337
|
+
|
|
338
|
+
Example:
|
|
339
|
+
model = load_model("models/classifier.pkl")
|
|
340
|
+
"""
|
|
341
|
+
return load_artifact(path)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# =============================================================================
|
|
345
|
+
# CONTAINER REGISTRY (Auto-routes to configured registry)
|
|
346
|
+
# =============================================================================
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def push_image(image_name: str, tag: str = "latest", local_image: str = None) -> str | None:
|
|
350
|
+
"""Push a Docker image to the configured registry.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
image_name: Name for the image in the registry.
|
|
354
|
+
tag: Image tag.
|
|
355
|
+
local_image: Local image name to push.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Full image URI.
|
|
359
|
+
|
|
360
|
+
Example:
|
|
361
|
+
push_image("ml-training", tag="v1.0", local_image="my-local-image")
|
|
362
|
+
"""
|
|
363
|
+
registry = get_container_registry()
|
|
364
|
+
|
|
365
|
+
if registry:
|
|
366
|
+
return registry.push_image(image_name, tag, local_image)
|
|
367
|
+
else:
|
|
368
|
+
logger.warning("No container_registry configured in flowyml.yaml")
|
|
369
|
+
return None
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def get_image_uri(image_name: str, tag: str = "latest") -> str | None:
|
|
373
|
+
"""Get the full URI for an image.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
image_name: Name of the image.
|
|
377
|
+
tag: Image tag.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Full image URI.
|
|
381
|
+
"""
|
|
382
|
+
registry = get_container_registry()
|
|
383
|
+
return registry.get_image_uri(image_name, tag) if registry else None
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# =============================================================================
|
|
387
|
+
# PIPELINE ORCHESTRATION (Auto-routes to configured orchestrator)
|
|
388
|
+
# =============================================================================
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def run_pipeline(
|
|
392
|
+
pipeline: Any,
|
|
393
|
+
run_id: str,
|
|
394
|
+
parameters: dict = None,
|
|
395
|
+
**kwargs,
|
|
396
|
+
) -> Any | None:
|
|
397
|
+
"""Run a pipeline on the configured orchestrator.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
pipeline: The pipeline to run.
|
|
401
|
+
run_id: Unique identifier for this run.
|
|
402
|
+
parameters: Pipeline parameters.
|
|
403
|
+
**kwargs: Additional orchestrator-specific arguments.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Run result/job object.
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
run_pipeline(my_training_pipeline, "training-001",
|
|
410
|
+
parameters={"epochs": 100})
|
|
411
|
+
"""
|
|
412
|
+
orchestrator = get_orchestrator()
|
|
413
|
+
|
|
414
|
+
if orchestrator:
|
|
415
|
+
return orchestrator.run_pipeline(pipeline, run_id, parameters=parameters, **kwargs)
|
|
416
|
+
else:
|
|
417
|
+
logger.warning("No orchestrator configured in flowyml.yaml")
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
# =============================================================================
|
|
422
|
+
# ALERTS (Auto-routes to configured alerter)
|
|
423
|
+
# =============================================================================
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def send_alert(title: str, message: str, level: str = "info") -> bool:
|
|
427
|
+
"""Send an alert notification.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
title: Alert title.
|
|
431
|
+
message: Alert message.
|
|
432
|
+
level: Alert level (info, warning, error, critical).
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
True if alert was sent successfully.
|
|
436
|
+
|
|
437
|
+
Example:
|
|
438
|
+
send_alert("Training Complete", "Model accuracy: 95%", level="success")
|
|
439
|
+
"""
|
|
440
|
+
alerter = get_alerter()
|
|
441
|
+
|
|
442
|
+
if alerter:
|
|
443
|
+
return alerter.send_alert(title, message, level)
|
|
444
|
+
else:
|
|
445
|
+
logger.info(f"[{level.upper()}] {title}: {message}")
|
|
446
|
+
return True
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# =============================================================================
|
|
450
|
+
# STACK INFO
|
|
451
|
+
# =============================================================================
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def show_stack() -> dict[str, str]:
|
|
455
|
+
"""Show the currently configured stack.
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
Dictionary of configured plugins.
|
|
459
|
+
|
|
460
|
+
Example:
|
|
461
|
+
stack = show_stack()
|
|
462
|
+
print(stack)
|
|
463
|
+
# {'experiment_tracker': 'mlflow', 'artifact_store': 'gcs', ...}
|
|
464
|
+
"""
|
|
465
|
+
config = get_config()
|
|
466
|
+
plugins_config = config.plugins_config
|
|
467
|
+
|
|
468
|
+
stack = {}
|
|
469
|
+
for role, conf in plugins_config.items():
|
|
470
|
+
if isinstance(conf, dict):
|
|
471
|
+
stack[role] = conf.get("type", "unknown")
|
|
472
|
+
|
|
473
|
+
return stack
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def validate_stack() -> dict[str, bool]:
|
|
477
|
+
"""Validate that all configured plugins are installed.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
Dictionary mapping plugin roles to installation status.
|
|
481
|
+
"""
|
|
482
|
+
from flowyml.plugins import is_installed
|
|
483
|
+
|
|
484
|
+
config = get_config()
|
|
485
|
+
plugins_config = config.plugins_config
|
|
486
|
+
|
|
487
|
+
results = {}
|
|
488
|
+
for role, conf in plugins_config.items():
|
|
489
|
+
if isinstance(conf, dict):
|
|
490
|
+
plugin_type = conf.get("type")
|
|
491
|
+
if plugin_type:
|
|
492
|
+
results[role] = is_installed(plugin_type)
|
|
493
|
+
|
|
494
|
+
return results
|