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.
Files changed (126) hide show
  1. flowyml/assets/base.py +15 -0
  2. flowyml/assets/metrics.py +5 -0
  3. flowyml/cli/main.py +709 -0
  4. flowyml/cli/stack_cli.py +138 -25
  5. flowyml/core/__init__.py +17 -0
  6. flowyml/core/executor.py +161 -26
  7. flowyml/core/image_builder.py +129 -0
  8. flowyml/core/log_streamer.py +227 -0
  9. flowyml/core/orchestrator.py +22 -2
  10. flowyml/core/pipeline.py +34 -10
  11. flowyml/core/routing.py +558 -0
  12. flowyml/core/step.py +9 -1
  13. flowyml/core/step_grouping.py +49 -35
  14. flowyml/core/types.py +407 -0
  15. flowyml/monitoring/alerts.py +10 -0
  16. flowyml/monitoring/notifications.py +104 -25
  17. flowyml/monitoring/slack_blocks.py +323 -0
  18. flowyml/plugins/__init__.py +251 -0
  19. flowyml/plugins/alerters/__init__.py +1 -0
  20. flowyml/plugins/alerters/slack.py +168 -0
  21. flowyml/plugins/base.py +752 -0
  22. flowyml/plugins/config.py +478 -0
  23. flowyml/plugins/deployers/__init__.py +22 -0
  24. flowyml/plugins/deployers/gcp_cloud_run.py +200 -0
  25. flowyml/plugins/deployers/sagemaker.py +306 -0
  26. flowyml/plugins/deployers/vertex.py +290 -0
  27. flowyml/plugins/integration.py +369 -0
  28. flowyml/plugins/manager.py +510 -0
  29. flowyml/plugins/model_registries/__init__.py +22 -0
  30. flowyml/plugins/model_registries/mlflow.py +159 -0
  31. flowyml/plugins/model_registries/sagemaker.py +489 -0
  32. flowyml/plugins/model_registries/vertex.py +386 -0
  33. flowyml/plugins/orchestrators/__init__.py +13 -0
  34. flowyml/plugins/orchestrators/sagemaker.py +443 -0
  35. flowyml/plugins/orchestrators/vertex_ai.py +461 -0
  36. flowyml/plugins/registries/__init__.py +13 -0
  37. flowyml/plugins/registries/ecr.py +321 -0
  38. flowyml/plugins/registries/gcr.py +313 -0
  39. flowyml/plugins/registry.py +454 -0
  40. flowyml/plugins/stack.py +494 -0
  41. flowyml/plugins/stack_config.py +537 -0
  42. flowyml/plugins/stores/__init__.py +13 -0
  43. flowyml/plugins/stores/gcs.py +460 -0
  44. flowyml/plugins/stores/s3.py +453 -0
  45. flowyml/plugins/trackers/__init__.py +11 -0
  46. flowyml/plugins/trackers/mlflow.py +316 -0
  47. flowyml/plugins/validators/__init__.py +3 -0
  48. flowyml/plugins/validators/deepchecks.py +119 -0
  49. flowyml/registry/__init__.py +2 -1
  50. flowyml/registry/model_environment.py +109 -0
  51. flowyml/registry/model_registry.py +241 -96
  52. flowyml/serving/__init__.py +17 -0
  53. flowyml/serving/model_server.py +628 -0
  54. flowyml/stacks/__init__.py +60 -0
  55. flowyml/stacks/aws.py +93 -0
  56. flowyml/stacks/base.py +62 -0
  57. flowyml/stacks/components.py +12 -0
  58. flowyml/stacks/gcp.py +44 -9
  59. flowyml/stacks/plugins.py +115 -0
  60. flowyml/stacks/registry.py +2 -1
  61. flowyml/storage/sql.py +401 -12
  62. flowyml/tracking/experiment.py +8 -5
  63. flowyml/ui/backend/Dockerfile +87 -16
  64. flowyml/ui/backend/auth.py +12 -2
  65. flowyml/ui/backend/main.py +149 -5
  66. flowyml/ui/backend/routers/ai_context.py +226 -0
  67. flowyml/ui/backend/routers/assets.py +23 -4
  68. flowyml/ui/backend/routers/auth.py +96 -0
  69. flowyml/ui/backend/routers/deployments.py +660 -0
  70. flowyml/ui/backend/routers/model_explorer.py +597 -0
  71. flowyml/ui/backend/routers/plugins.py +103 -51
  72. flowyml/ui/backend/routers/projects.py +91 -8
  73. flowyml/ui/backend/routers/runs.py +20 -1
  74. flowyml/ui/backend/routers/schedules.py +22 -17
  75. flowyml/ui/backend/routers/templates.py +319 -0
  76. flowyml/ui/backend/routers/websocket.py +2 -2
  77. flowyml/ui/frontend/Dockerfile +55 -6
  78. flowyml/ui/frontend/dist/assets/index-B5AsPTSz.css +1 -0
  79. flowyml/ui/frontend/dist/assets/index-dFbZ8wD8.js +753 -0
  80. flowyml/ui/frontend/dist/index.html +2 -2
  81. flowyml/ui/frontend/dist/logo.png +0 -0
  82. flowyml/ui/frontend/nginx.conf +65 -4
  83. flowyml/ui/frontend/package-lock.json +1404 -74
  84. flowyml/ui/frontend/package.json +3 -0
  85. flowyml/ui/frontend/public/logo.png +0 -0
  86. flowyml/ui/frontend/src/App.jsx +10 -7
  87. flowyml/ui/frontend/src/app/auth/Login.jsx +90 -0
  88. flowyml/ui/frontend/src/app/dashboard/page.jsx +8 -8
  89. flowyml/ui/frontend/src/app/deployments/page.jsx +786 -0
  90. flowyml/ui/frontend/src/app/model-explorer/page.jsx +1031 -0
  91. flowyml/ui/frontend/src/app/pipelines/page.jsx +12 -2
  92. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +19 -6
  93. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +36 -24
  94. flowyml/ui/frontend/src/app/runs/page.jsx +8 -2
  95. flowyml/ui/frontend/src/app/settings/page.jsx +267 -253
  96. flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +29 -7
  97. flowyml/ui/frontend/src/components/Layout.jsx +6 -0
  98. flowyml/ui/frontend/src/components/PipelineGraph.jsx +79 -29
  99. flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +36 -6
  100. flowyml/ui/frontend/src/components/RunMetaPanel.jsx +113 -0
  101. flowyml/ui/frontend/src/components/ai/AIAssistantButton.jsx +71 -0
  102. flowyml/ui/frontend/src/components/ai/AIAssistantPanel.jsx +420 -0
  103. flowyml/ui/frontend/src/components/header/Header.jsx +22 -0
  104. flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +4 -4
  105. flowyml/ui/frontend/src/components/plugins/{ZenMLIntegration.jsx → StackImport.jsx} +38 -12
  106. flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +36 -13
  107. flowyml/ui/frontend/src/contexts/AIAssistantContext.jsx +245 -0
  108. flowyml/ui/frontend/src/contexts/AuthContext.jsx +108 -0
  109. flowyml/ui/frontend/src/hooks/useAIContext.js +156 -0
  110. flowyml/ui/frontend/src/hooks/useWebGPU.js +54 -0
  111. flowyml/ui/frontend/src/layouts/MainLayout.jsx +6 -0
  112. flowyml/ui/frontend/src/router/index.jsx +47 -20
  113. flowyml/ui/frontend/src/services/pluginService.js +3 -1
  114. flowyml/ui/server_manager.py +5 -5
  115. flowyml/ui/utils.py +157 -39
  116. flowyml/utils/config.py +37 -15
  117. flowyml/utils/model_introspection.py +123 -0
  118. flowyml/utils/observability.py +30 -0
  119. flowyml-1.8.0.dist-info/METADATA +174 -0
  120. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/RECORD +123 -65
  121. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/WHEEL +1 -1
  122. flowyml/ui/frontend/dist/assets/index-B40RsQDq.css +0 -1
  123. flowyml/ui/frontend/dist/assets/index-CjI0zKCn.js +0 -685
  124. flowyml-1.7.2.dist-info/METADATA +0 -477
  125. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/entry_points.txt +0 -0
  126. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,369 @@
1
+ """FlowyML Pipeline-Plugin Integration.
2
+
3
+ This module integrates the native plugin system with FlowyML pipelines,
4
+ allowing pipelines to automatically use the configured stack for:
5
+ - Experiment tracking (log params, metrics)
6
+ - Artifact storage (save outputs)
7
+ - Model registry (register models)
8
+ - Orchestration (run on configured platform)
9
+
10
+ Usage:
11
+ # flowyml.yaml
12
+ plugins:
13
+ experiment_tracker:
14
+ type: mlflow
15
+ artifact_store:
16
+ type: gcs
17
+ bucket: my-ml-artifacts
18
+
19
+ # In code - pipeline automatically uses configured stack
20
+ from flowyml import pipeline, step
21
+ from flowyml.plugins.integration import run_with_stack
22
+
23
+ @pipeline
24
+ def training_pipeline():
25
+ data = load_data()
26
+ model = train(data)
27
+ return model
28
+
29
+ # Run with automatic tracking and artifact storage
30
+ result = run_with_stack(training_pipeline)
31
+ """
32
+
33
+ import logging
34
+ from typing import Any
35
+ from functools import wraps
36
+ import time
37
+
38
+ from flowyml.plugins.stack import (
39
+ start_run,
40
+ end_run,
41
+ log_params,
42
+ log_metrics,
43
+ set_tag,
44
+ save_artifact,
45
+ save_model,
46
+ )
47
+
48
+ logger = logging.getLogger(__name__)
49
+
50
+
51
+ class StackContext:
52
+ """Context manager for running code with automatic stack integration.
53
+
54
+ Automatically:
55
+ - Starts an experiment run
56
+ - Logs timing and metadata
57
+ - Saves artifacts to configured store
58
+ - Ends the run with appropriate status
59
+
60
+ Example:
61
+ with StackContext("my_training") as ctx:
62
+ # Your code here - automatically tracked
63
+ model = train()
64
+ ctx.log_model(model, "classifier")
65
+ """
66
+
67
+ def __init__(
68
+ self,
69
+ run_name: str,
70
+ experiment_name: str = None,
71
+ tags: dict = None,
72
+ log_system_info: bool = True,
73
+ ):
74
+ """Initialize the stack context.
75
+
76
+ Args:
77
+ run_name: Name for this run.
78
+ experiment_name: Optional experiment name.
79
+ tags: Optional tags for the run.
80
+ log_system_info: If True, log system information.
81
+ """
82
+ self.run_name = run_name
83
+ self.experiment_name = experiment_name
84
+ self.tags = tags or {}
85
+ self.log_system_info = log_system_info
86
+ self._run_id = None
87
+ self._start_time = None
88
+ self._artifacts = []
89
+ self._models = []
90
+
91
+ def __enter__(self) -> "StackContext":
92
+ """Start the run."""
93
+ self._start_time = time.time()
94
+ self._run_id = start_run(
95
+ self.run_name,
96
+ experiment_name=self.experiment_name,
97
+ tags=self.tags,
98
+ )
99
+
100
+ if self.log_system_info:
101
+ self._log_system_info()
102
+
103
+ return self
104
+
105
+ def __exit__(self, exc_type, exc_val, exc_tb):
106
+ """End the run."""
107
+ duration = time.time() - self._start_time
108
+
109
+ # Log final metrics
110
+ log_metrics({"duration_seconds": duration})
111
+
112
+ if exc_type:
113
+ set_tag("status", "FAILED")
114
+ set_tag("error_type", exc_type.__name__)
115
+ end_run("FAILED")
116
+ else:
117
+ set_tag("status", "COMPLETED")
118
+ end_run("FINISHED")
119
+
120
+ return False # Don't suppress exceptions
121
+
122
+ def _log_system_info(self):
123
+ """Log system information."""
124
+ import platform
125
+ import sys
126
+
127
+ try:
128
+ log_params(
129
+ {
130
+ "python_version": sys.version.split()[0],
131
+ "platform": platform.system(),
132
+ "machine": platform.machine(),
133
+ },
134
+ )
135
+ except Exception as e:
136
+ logger.debug(f"Could not log system info: {e}")
137
+
138
+ def log_params(self, params: dict) -> None:
139
+ """Log parameters."""
140
+ log_params(params)
141
+
142
+ def log_metrics(self, metrics: dict, step: int = None) -> None:
143
+ """Log metrics."""
144
+ log_metrics(metrics, step)
145
+
146
+ def save_artifact(self, artifact: Any, path: str) -> str:
147
+ """Save an artifact and track it.
148
+
149
+ Returns:
150
+ URI of the saved artifact.
151
+ """
152
+ uri = save_artifact(artifact, path)
153
+ if uri:
154
+ self._artifacts.append(uri)
155
+ set_tag(f"artifact_{len(self._artifacts)}", uri)
156
+ return uri
157
+
158
+ def save_model(self, model: Any, path: str, model_type: str = None) -> str:
159
+ """Save a model and track it.
160
+
161
+ Returns:
162
+ URI of the saved model.
163
+ """
164
+ uri = save_model(model, path, model_type=model_type)
165
+ if uri:
166
+ self._models.append(uri)
167
+ set_tag(f"model_{len(self._models)}", uri)
168
+ return uri
169
+
170
+
171
+ def run_with_stack(
172
+ pipeline_or_func,
173
+ run_name: str = None,
174
+ experiment_name: str = None,
175
+ parameters: dict = None,
176
+ tags: dict = None,
177
+ ):
178
+ """Run a pipeline or function with automatic stack integration.
179
+
180
+ This wraps the execution with:
181
+ - Automatic experiment tracking
182
+ - Parameter logging
183
+ - Timing and metrics
184
+ - Error handling
185
+
186
+ Args:
187
+ pipeline_or_func: Pipeline or callable to run.
188
+ run_name: Name for this run (defaults to function name).
189
+ experiment_name: Optional experiment name.
190
+ parameters: Parameters to pass and log.
191
+ tags: Optional tags for the run.
192
+
193
+ Returns:
194
+ Result of the pipeline/function.
195
+
196
+ Example:
197
+ @pipeline
198
+ def my_training():
199
+ ...
200
+
201
+ # Run with full stack integration
202
+ result = run_with_stack(
203
+ my_training,
204
+ run_name="training_v1",
205
+ parameters={"lr": 0.001}
206
+ )
207
+ """
208
+ # Determine run name
209
+ if run_name is None:
210
+ if hasattr(pipeline_or_func, "name"):
211
+ run_name = pipeline_or_func.name
212
+ elif hasattr(pipeline_or_func, "__name__"):
213
+ run_name = pipeline_or_func.__name__
214
+ else:
215
+ run_name = "unnamed_run"
216
+
217
+ parameters = parameters or {}
218
+ tags = tags or {}
219
+
220
+ with StackContext(run_name, experiment_name, tags) as ctx:
221
+ # Log parameters
222
+ if parameters:
223
+ ctx.log_params(parameters)
224
+
225
+ # Execute
226
+ if parameters:
227
+ result = pipeline_or_func(**parameters)
228
+ else:
229
+ result = pipeline_or_func()
230
+
231
+ return result
232
+
233
+
234
+ def tracked(
235
+ experiment_name: str = None,
236
+ log_params: bool = True,
237
+ log_result: bool = True,
238
+ ):
239
+ """Decorator to add automatic tracking to any function.
240
+
241
+ The decorated function will automatically:
242
+ - Start an experiment run
243
+ - Log function parameters
244
+ - Log execution time
245
+ - End the run with status
246
+
247
+ Args:
248
+ experiment_name: Optional experiment name.
249
+ log_params: If True, log function parameters.
250
+ log_result: If True, log result summary.
251
+
252
+ Example:
253
+ @tracked(experiment_name="model_training")
254
+ def train_model(lr=0.001, epochs=100):
255
+ model = ...
256
+ return model
257
+ """
258
+
259
+ def decorator(func):
260
+ @wraps(func)
261
+ def wrapper(*args, **kwargs):
262
+ run_name = func.__name__
263
+
264
+ with StackContext(run_name, experiment_name) as ctx:
265
+ # Log parameters
266
+ if log_params and kwargs:
267
+ ctx.log_params(kwargs)
268
+
269
+ # Execute
270
+ result = func(*args, **kwargs)
271
+
272
+ # Log result summary if applicable
273
+ if log_result and isinstance(result, dict):
274
+ metrics = {k: v for k, v in result.items() if isinstance(v, (int, float))}
275
+ if metrics:
276
+ ctx.log_metrics(metrics)
277
+
278
+ return result
279
+
280
+ return wrapper
281
+
282
+ return decorator
283
+
284
+
285
+ class PipelinePluginIntegration:
286
+ """Integration layer between FlowyML pipelines and plugins.
287
+
288
+ This class provides hooks for the pipeline executor to automatically
289
+ use the configured stack.
290
+ """
291
+
292
+ def __init__(self):
293
+ """Initialize the integration."""
294
+ self._current_run = None
295
+ self._step_outputs = {}
296
+
297
+ def on_pipeline_start(self, pipeline_name: str, context: dict = None):
298
+ """Called when a pipeline starts."""
299
+ self._current_run = start_run(
300
+ pipeline_name,
301
+ tags={"type": "pipeline"},
302
+ )
303
+
304
+ if context:
305
+ log_params(context)
306
+
307
+ def on_pipeline_end(self, success: bool, error: Exception = None):
308
+ """Called when a pipeline ends."""
309
+ if success:
310
+ end_run("FINISHED")
311
+ else:
312
+ if error:
313
+ set_tag("error", str(error))
314
+ end_run("FAILED")
315
+
316
+ self._current_run = None
317
+
318
+ def on_step_start(self, step_name: str, inputs: dict = None):
319
+ """Called when a step starts."""
320
+ set_tag(f"step_{step_name}_status", "started")
321
+
322
+ if inputs:
323
+ # Log input sizes/shapes if applicable
324
+ for key, value in inputs.items():
325
+ if hasattr(value, "shape"):
326
+ set_tag(f"input_{key}_shape", str(value.shape))
327
+
328
+ def on_step_end(
329
+ self,
330
+ step_name: str,
331
+ outputs: dict = None,
332
+ duration: float = None,
333
+ cached: bool = False,
334
+ ):
335
+ """Called when a step ends."""
336
+ set_tag(f"step_{step_name}_status", "completed")
337
+
338
+ if duration:
339
+ log_metrics({f"{step_name}_duration": duration})
340
+
341
+ if cached:
342
+ set_tag(f"step_{step_name}_cached", "true")
343
+
344
+ # Store outputs for later saving
345
+ if outputs:
346
+ self._step_outputs[step_name] = outputs
347
+
348
+ def on_step_error(self, step_name: str, error: Exception):
349
+ """Called when a step errors."""
350
+ set_tag(f"step_{step_name}_status", "failed")
351
+ set_tag(f"step_{step_name}_error", str(error))
352
+
353
+ def save_step_outputs(self, step_name: str, outputs: dict):
354
+ """Save step outputs to the artifact store."""
355
+ for output_name, value in outputs.items():
356
+ path = f"steps/{step_name}/{output_name}"
357
+ save_artifact(value, path)
358
+
359
+
360
+ # Global integration instance
361
+ _integration: PipelinePluginIntegration | None = None
362
+
363
+
364
+ def get_integration() -> PipelinePluginIntegration:
365
+ """Get the global pipeline-plugin integration."""
366
+ global _integration
367
+ if _integration is None:
368
+ _integration = PipelinePluginIntegration()
369
+ return _integration