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
|
@@ -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
|