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,168 @@
1
+ """Slack Alerter - Native FlowyML Plugin.
2
+
3
+ This plugin allows sending alerts and notifications to Slack channels
4
+ using incoming webhooks or bot tokens.
5
+ """
6
+
7
+ import logging
8
+ import json
9
+ import urllib.request
10
+ import urllib.error
11
+
12
+ from flowyml.plugins.base import AlerterPlugin, PluginMetadata, PluginType
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class SlackAlerter(AlerterPlugin):
18
+ """Native Slack alerter for FlowyML.
19
+
20
+ Sends notifications to Slack.
21
+
22
+ Args:
23
+ webhook_url: Slack incoming webhook URL (optional if token provided).
24
+ token: Slack bot token (optional if webhook provided).
25
+ default_channel: Default channel to post to (required if using token).
26
+ """
27
+
28
+ metadata = PluginMetadata(
29
+ name="slack",
30
+ version="1.0.0",
31
+ description="Slack Alerter for pipeline notifications",
32
+ author="FlowyML Team",
33
+ plugin_type=PluginType.ALERTER,
34
+ tags=["notification", "slack", "ops"],
35
+ )
36
+
37
+ def __init__(
38
+ self,
39
+ webhook_url: str = None,
40
+ token: str = None,
41
+ default_channel: str = None,
42
+ **kwargs,
43
+ ):
44
+ super().__init__(**kwargs)
45
+ self.webhook_url = webhook_url
46
+ self.token = token
47
+ self.default_channel = default_channel
48
+
49
+ @property
50
+ def plugin_type(self) -> PluginType:
51
+ return PluginType.ALERTER
52
+
53
+ def validate(self) -> bool:
54
+ """Validate configuration."""
55
+ if not self.webhook_url and not self.token:
56
+ raise ValueError("SlackAlerter requires either 'webhook_url' or 'token'.")
57
+ if self.token and not self.default_channel:
58
+ raise ValueError("SlackAlerter with 'token' requires 'default_channel'.")
59
+ return True
60
+
61
+ def send_alert(
62
+ self,
63
+ title: str,
64
+ message: str,
65
+ level: str = "info",
66
+ channel: str = None,
67
+ **kwargs,
68
+ ) -> bool:
69
+ """Send an alert to Slack.
70
+
71
+ Args:
72
+ title: Title of the alert.
73
+ message: Main message body.
74
+ level: Alert level (info, success, warning, error).
75
+ channel: Target channel (overrides default).
76
+ **kwargs: Additional Slack payload options (attachments, blocks, etc.).
77
+
78
+ Returns:
79
+ True if successful.
80
+ """
81
+ color_map = {
82
+ "info": "#3498db", # Blue
83
+ "success": "#2ecc71", # Green
84
+ "warning": "#f1c40f", # Yellow
85
+ "error": "#e74c3c", # Red
86
+ "critical": "#c0392b", # Dark Red
87
+ }
88
+ color = color_map.get(level.lower(), "#3498db")
89
+
90
+ # Construct payload
91
+ payload = {
92
+ "attachments": [
93
+ {
94
+ "color": color,
95
+ "title": title,
96
+ "text": message,
97
+ "mrkdwn_in": ["text"],
98
+ "fields": [
99
+ {"title": "Level", "value": level.upper(), "short": True},
100
+ ],
101
+ },
102
+ ],
103
+ }
104
+
105
+ # Determine method: Webhook vs Token
106
+ if self.webhook_url:
107
+ return self._send_via_webhook(payload)
108
+ elif self.token:
109
+ target_channel = channel or self.default_channel
110
+ payload["channel"] = target_channel
111
+ return self._send_via_api(payload)
112
+
113
+ return False
114
+
115
+ def _send_via_webhook(self, payload: dict) -> bool:
116
+ """Send using Incoming Webhook."""
117
+ try:
118
+ if not self.webhook_url.startswith(("http://", "https://")):
119
+ raise ValueError("Slack webhook URL must start with http:// or https://")
120
+
121
+ data = json.dumps(payload).encode("utf-8")
122
+ req = urllib.request.Request( # noqa: S310
123
+ self.webhook_url,
124
+ data=data,
125
+ headers={"Content-Type": "application/json"},
126
+ )
127
+ with urllib.request.urlopen(req) as response: # noqa: S310
128
+ if response.status == 200:
129
+ logger.info("Slack alert sent successfully (webhook).")
130
+ return True
131
+ else:
132
+ logger.error(f"Slack webhook failed: {response.status} {response.read()}")
133
+ return False
134
+ except Exception as e:
135
+ logger.error(f"Failed to send Slack alert: {e}")
136
+ return False
137
+
138
+ def _send_via_api(self, payload: dict) -> bool:
139
+ """Send using Slack Web API (chat.postMessage)."""
140
+ try:
141
+ url = "https://slack.com/api/chat.postMessage"
142
+
143
+ # Formatting for API differs slightly from pure attachments
144
+ api_payload = {
145
+ "channel": payload.pop("channel"),
146
+ "attachments": payload["attachments"],
147
+ }
148
+
149
+ data = json.dumps(api_payload).encode("utf-8")
150
+ req = urllib.request.Request( # noqa: S310
151
+ url,
152
+ data=data,
153
+ headers={
154
+ "Content-Type": "application/json",
155
+ "Authorization": f"Bearer {self.token}",
156
+ },
157
+ )
158
+ with urllib.request.urlopen(req) as response: # noqa: S310
159
+ resp_body = json.loads(response.read().decode())
160
+ if resp_body.get("ok"):
161
+ logger.info("Slack alert sent successfully (API).")
162
+ return True
163
+ else:
164
+ logger.error(f"Slack API failed: {resp_body.get('error')}")
165
+ return False
166
+ except Exception as e:
167
+ logger.error(f"Failed to send Slack alert (API): {e}")
168
+ return False