mlrun 1.7.2rc3__py3-none-any.whl → 1.8.0rc2__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.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__init__.py +18 -18
- mlrun/__main__.py +3 -3
- mlrun/alerts/alert.py +19 -12
- mlrun/artifacts/__init__.py +0 -2
- mlrun/artifacts/base.py +34 -11
- mlrun/artifacts/dataset.py +16 -16
- mlrun/artifacts/manager.py +13 -13
- mlrun/artifacts/model.py +66 -53
- mlrun/common/constants.py +6 -0
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/feature_set.py +1 -0
- mlrun/common/formatters/function.py +1 -0
- mlrun/common/formatters/model_endpoint.py +30 -0
- mlrun/common/formatters/pipeline.py +1 -2
- mlrun/common/formatters/project.py +9 -0
- mlrun/common/model_monitoring/__init__.py +0 -3
- mlrun/common/model_monitoring/helpers.py +1 -1
- mlrun/common/runtimes/constants.py +1 -2
- mlrun/common/schemas/__init__.py +7 -2
- mlrun/common/schemas/alert.py +31 -18
- mlrun/common/schemas/api_gateway.py +3 -3
- mlrun/common/schemas/artifact.py +7 -13
- mlrun/common/schemas/auth.py +6 -4
- mlrun/common/schemas/background_task.py +7 -7
- mlrun/common/schemas/client_spec.py +2 -2
- mlrun/common/schemas/clusterization_spec.py +2 -2
- mlrun/common/schemas/common.py +53 -3
- mlrun/common/schemas/datastore_profile.py +1 -1
- mlrun/common/schemas/feature_store.py +9 -9
- mlrun/common/schemas/frontend_spec.py +4 -4
- mlrun/common/schemas/function.py +10 -10
- mlrun/common/schemas/hub.py +1 -1
- mlrun/common/schemas/k8s.py +3 -3
- mlrun/common/schemas/memory_reports.py +3 -3
- mlrun/common/schemas/model_monitoring/__init__.py +8 -1
- mlrun/common/schemas/model_monitoring/constants.py +62 -12
- mlrun/common/schemas/model_monitoring/grafana.py +1 -1
- mlrun/common/schemas/model_monitoring/model_endpoint_v2.py +149 -0
- mlrun/common/schemas/model_monitoring/model_endpoints.py +22 -6
- mlrun/common/schemas/notification.py +18 -3
- mlrun/common/schemas/object.py +1 -1
- mlrun/common/schemas/pagination.py +4 -4
- mlrun/common/schemas/partition.py +137 -0
- mlrun/common/schemas/pipeline.py +2 -2
- mlrun/common/schemas/project.py +22 -17
- mlrun/common/schemas/runs.py +2 -2
- mlrun/common/schemas/runtime_resource.py +5 -5
- mlrun/common/schemas/schedule.py +1 -1
- mlrun/common/schemas/secret.py +1 -1
- mlrun/common/schemas/tag.py +3 -3
- mlrun/common/schemas/workflow.py +5 -5
- mlrun/config.py +65 -15
- mlrun/data_types/__init__.py +0 -2
- mlrun/data_types/data_types.py +0 -1
- mlrun/data_types/infer.py +3 -1
- mlrun/data_types/spark.py +4 -4
- mlrun/data_types/to_pandas.py +2 -11
- mlrun/datastore/__init__.py +0 -2
- mlrun/datastore/alibaba_oss.py +4 -1
- mlrun/datastore/azure_blob.py +4 -1
- mlrun/datastore/base.py +12 -4
- mlrun/datastore/datastore.py +9 -3
- mlrun/datastore/datastore_profile.py +20 -20
- mlrun/datastore/dbfs_store.py +4 -1
- mlrun/datastore/filestore.py +4 -1
- mlrun/datastore/google_cloud_storage.py +4 -1
- mlrun/datastore/hdfs.py +4 -1
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +4 -1
- mlrun/datastore/s3.py +4 -1
- mlrun/datastore/sources.py +51 -49
- mlrun/datastore/store_resources.py +0 -2
- mlrun/datastore/targets.py +22 -23
- mlrun/datastore/utils.py +2 -2
- mlrun/datastore/v3io.py +4 -1
- mlrun/datastore/wasbfs/fs.py +13 -12
- mlrun/db/base.py +170 -64
- mlrun/db/factory.py +3 -0
- mlrun/db/httpdb.py +986 -238
- mlrun/db/nopdb.py +155 -57
- mlrun/errors.py +2 -2
- mlrun/execution.py +55 -29
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +40 -40
- mlrun/feature_store/common.py +9 -9
- mlrun/feature_store/feature_set.py +20 -18
- mlrun/feature_store/feature_vector.py +27 -24
- mlrun/feature_store/retrieval/base.py +14 -9
- mlrun/feature_store/retrieval/job.py +2 -1
- mlrun/feature_store/steps.py +2 -2
- mlrun/features.py +30 -13
- mlrun/frameworks/__init__.py +1 -2
- mlrun/frameworks/_common/__init__.py +1 -2
- mlrun/frameworks/_common/artifacts_library.py +2 -2
- mlrun/frameworks/_common/mlrun_interface.py +10 -6
- mlrun/frameworks/_common/model_handler.py +29 -27
- mlrun/frameworks/_common/producer.py +3 -1
- mlrun/frameworks/_dl_common/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
- mlrun/frameworks/_ml_common/__init__.py +1 -2
- mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_ml_common/model_handler.py +21 -21
- mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/auto_mlrun/__init__.py +1 -2
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
- mlrun/frameworks/huggingface/__init__.py +1 -2
- mlrun/frameworks/huggingface/model_server.py +9 -9
- mlrun/frameworks/lgbm/__init__.py +47 -44
- mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
- mlrun/frameworks/lgbm/model_handler.py +15 -11
- mlrun/frameworks/lgbm/model_server.py +11 -7
- mlrun/frameworks/lgbm/utils.py +2 -2
- mlrun/frameworks/onnx/__init__.py +1 -2
- mlrun/frameworks/onnx/dataset.py +3 -3
- mlrun/frameworks/onnx/mlrun_interface.py +2 -2
- mlrun/frameworks/onnx/model_handler.py +7 -5
- mlrun/frameworks/onnx/model_server.py +8 -6
- mlrun/frameworks/parallel_coordinates.py +11 -11
- mlrun/frameworks/pytorch/__init__.py +22 -23
- mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
- mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
- mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
- mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
- mlrun/frameworks/pytorch/model_handler.py +21 -17
- mlrun/frameworks/pytorch/model_server.py +13 -9
- mlrun/frameworks/sklearn/__init__.py +19 -18
- mlrun/frameworks/sklearn/estimator.py +2 -2
- mlrun/frameworks/sklearn/metric.py +3 -3
- mlrun/frameworks/sklearn/metrics_library.py +8 -6
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
- mlrun/frameworks/sklearn/model_handler.py +4 -3
- mlrun/frameworks/tf_keras/__init__.py +11 -12
- mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
- mlrun/frameworks/tf_keras/model_handler.py +17 -13
- mlrun/frameworks/tf_keras/model_server.py +12 -8
- mlrun/frameworks/xgboost/__init__.py +19 -18
- mlrun/frameworks/xgboost/model_handler.py +13 -9
- mlrun/launcher/base.py +3 -4
- mlrun/launcher/local.py +1 -1
- mlrun/launcher/remote.py +1 -1
- mlrun/lists.py +4 -3
- mlrun/model.py +110 -46
- mlrun/model_monitoring/__init__.py +1 -2
- mlrun/model_monitoring/api.py +6 -6
- mlrun/model_monitoring/applications/_application_steps.py +13 -15
- mlrun/model_monitoring/applications/histogram_data_drift.py +41 -15
- mlrun/model_monitoring/applications/results.py +55 -3
- mlrun/model_monitoring/controller.py +185 -223
- mlrun/model_monitoring/db/_schedules.py +156 -0
- mlrun/model_monitoring/db/_stats.py +189 -0
- mlrun/model_monitoring/db/stores/__init__.py +1 -1
- mlrun/model_monitoring/db/stores/base/store.py +6 -65
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -25
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -97
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +2 -58
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -15
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +6 -257
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +9 -271
- mlrun/model_monitoring/db/tsdb/base.py +76 -24
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +61 -6
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +253 -28
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -17
- mlrun/model_monitoring/helpers.py +91 -1
- mlrun/model_monitoring/model_endpoint.py +4 -2
- mlrun/model_monitoring/stream_processing.py +16 -13
- mlrun/model_monitoring/tracking_policy.py +10 -3
- mlrun/model_monitoring/writer.py +47 -26
- mlrun/package/__init__.py +3 -6
- mlrun/package/context_handler.py +1 -1
- mlrun/package/packager.py +12 -9
- mlrun/package/packagers/__init__.py +0 -2
- mlrun/package/packagers/default_packager.py +14 -11
- mlrun/package/packagers/numpy_packagers.py +16 -7
- mlrun/package/packagers/pandas_packagers.py +18 -18
- mlrun/package/packagers/python_standard_library_packagers.py +25 -11
- mlrun/package/packagers_manager.py +31 -14
- mlrun/package/utils/__init__.py +0 -3
- mlrun/package/utils/_pickler.py +6 -6
- mlrun/platforms/__init__.py +3 -16
- mlrun/platforms/iguazio.py +4 -1
- mlrun/projects/operations.py +27 -27
- mlrun/projects/pipelines.py +34 -35
- mlrun/projects/project.py +535 -182
- mlrun/run.py +13 -10
- mlrun/runtimes/__init__.py +1 -3
- mlrun/runtimes/base.py +15 -11
- mlrun/runtimes/daskjob.py +9 -9
- mlrun/runtimes/generators.py +2 -1
- mlrun/runtimes/kubejob.py +4 -5
- mlrun/runtimes/mounts.py +572 -0
- mlrun/runtimes/mpijob/__init__.py +0 -2
- mlrun/runtimes/mpijob/abstract.py +7 -6
- mlrun/runtimes/nuclio/api_gateway.py +7 -7
- mlrun/runtimes/nuclio/application/application.py +11 -11
- mlrun/runtimes/nuclio/function.py +13 -13
- mlrun/runtimes/nuclio/serving.py +9 -9
- mlrun/runtimes/pod.py +154 -45
- mlrun/runtimes/remotesparkjob.py +3 -2
- mlrun/runtimes/sparkjob/__init__.py +0 -2
- mlrun/runtimes/sparkjob/spark3job.py +21 -11
- mlrun/runtimes/utils.py +6 -5
- mlrun/serving/merger.py +6 -4
- mlrun/serving/remote.py +18 -17
- mlrun/serving/routers.py +27 -27
- mlrun/serving/server.py +1 -1
- mlrun/serving/states.py +76 -71
- mlrun/serving/utils.py +13 -2
- mlrun/serving/v1_serving.py +3 -2
- mlrun/serving/v2_serving.py +4 -4
- mlrun/track/__init__.py +1 -1
- mlrun/track/tracker.py +2 -2
- mlrun/track/trackers/mlflow_tracker.py +6 -5
- mlrun/utils/async_http.py +1 -1
- mlrun/utils/helpers.py +70 -16
- mlrun/utils/logger.py +106 -4
- mlrun/utils/notifications/notification/__init__.py +22 -19
- mlrun/utils/notifications/notification/base.py +33 -14
- mlrun/utils/notifications/notification/console.py +6 -6
- mlrun/utils/notifications/notification/git.py +11 -11
- mlrun/utils/notifications/notification/ipython.py +10 -9
- mlrun/utils/notifications/notification/mail.py +149 -0
- mlrun/utils/notifications/notification/slack.py +6 -6
- mlrun/utils/notifications/notification/webhook.py +18 -22
- mlrun/utils/notifications/notification_pusher.py +43 -31
- mlrun/utils/regex.py +3 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc2.dist-info}/METADATA +18 -14
- mlrun-1.8.0rc2.dist-info/RECORD +358 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc2.dist-info}/WHEEL +1 -1
- mlrun-1.7.2rc3.dist-info/RECORD +0 -351
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc2.dist-info}/LICENSE +0 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc2.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Copyright 2023 Iguazio
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import re
|
|
15
|
+
import typing
|
|
16
|
+
from email.message import EmailMessage
|
|
17
|
+
|
|
18
|
+
import aiosmtplib
|
|
19
|
+
|
|
20
|
+
import mlrun.common.schemas
|
|
21
|
+
import mlrun.lists
|
|
22
|
+
import mlrun.utils.helpers
|
|
23
|
+
import mlrun.utils.notifications.notification.base as base
|
|
24
|
+
import mlrun.utils.regex
|
|
25
|
+
|
|
26
|
+
DEFAULT_SMTP_PORT = 587
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MailNotification(base.NotificationBase):
|
|
30
|
+
"""
|
|
31
|
+
API/Client notification for sending run statuses as a mail message
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
boolean_params = ["use_tls", "start_tls", "validate_certs"]
|
|
35
|
+
|
|
36
|
+
required_params = [
|
|
37
|
+
"server_host",
|
|
38
|
+
"server_port",
|
|
39
|
+
"sender_address",
|
|
40
|
+
"username",
|
|
41
|
+
"password",
|
|
42
|
+
"email_addresses",
|
|
43
|
+
] + boolean_params
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def validate_params(cls, params):
|
|
47
|
+
for required_param in cls.required_params:
|
|
48
|
+
if required_param not in params:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"Parameter '{required_param}' is required for MailNotification"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
for boolean_param in cls.boolean_params:
|
|
54
|
+
if not isinstance(params.get(boolean_param, None), bool):
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"Parameter '{boolean_param}' must be a boolean for MailNotification"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
cls._validate_emails(params)
|
|
60
|
+
|
|
61
|
+
async def push(
|
|
62
|
+
self,
|
|
63
|
+
message: str,
|
|
64
|
+
severity: typing.Optional[
|
|
65
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
66
|
+
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
67
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
68
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
69
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
70
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
71
|
+
):
|
|
72
|
+
self.params.setdefault("subject", f"[{severity}] {message}")
|
|
73
|
+
self.params.setdefault("body", message)
|
|
74
|
+
await self._send_email(**self.params)
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def enrich_default_params(
|
|
78
|
+
cls, params: dict, default_params: typing.Optional[dict] = None
|
|
79
|
+
) -> dict:
|
|
80
|
+
params = super().enrich_default_params(params, default_params)
|
|
81
|
+
params.setdefault("use_tls", True)
|
|
82
|
+
params.setdefault("start_tls", False)
|
|
83
|
+
params.setdefault("validate_certs", True)
|
|
84
|
+
params.setdefault("server_port", DEFAULT_SMTP_PORT)
|
|
85
|
+
|
|
86
|
+
default_mail_address = params.pop("default_email_addresses", "")
|
|
87
|
+
email_addresses = params.get("email_addresses", default_mail_address)
|
|
88
|
+
if isinstance(email_addresses, list):
|
|
89
|
+
email_addresses = ",".join(email_addresses)
|
|
90
|
+
params["email_addresses"] = email_addresses
|
|
91
|
+
|
|
92
|
+
return params
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def _validate_emails(cls, params):
|
|
96
|
+
cls._validate_email_address(params["sender_address"])
|
|
97
|
+
|
|
98
|
+
if not isinstance(params["email_addresses"], (str, list)):
|
|
99
|
+
raise ValueError(
|
|
100
|
+
"Parameter 'email_addresses' must be a string or a list of strings"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
email_addresses = params["email_addresses"]
|
|
104
|
+
if isinstance(email_addresses, str):
|
|
105
|
+
email_addresses = email_addresses.split(",")
|
|
106
|
+
for email_address in email_addresses:
|
|
107
|
+
cls._validate_email_address(email_address)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def _validate_email_address(cls, email_address):
|
|
111
|
+
if not isinstance(email_address, str):
|
|
112
|
+
raise ValueError(f"Email address '{email_address}' must be a string")
|
|
113
|
+
|
|
114
|
+
if not re.match(mlrun.utils.regex.mail_regex, email_address):
|
|
115
|
+
raise ValueError(f"Invalid email address '{email_address}'")
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
async def _send_email(
|
|
119
|
+
email_addresses: str,
|
|
120
|
+
sender_address: str,
|
|
121
|
+
server_host: str,
|
|
122
|
+
server_port: int,
|
|
123
|
+
username: str,
|
|
124
|
+
password: str,
|
|
125
|
+
use_tls: bool,
|
|
126
|
+
start_tls: bool,
|
|
127
|
+
validate_certs: bool,
|
|
128
|
+
subject: str,
|
|
129
|
+
body: str,
|
|
130
|
+
**kwargs,
|
|
131
|
+
):
|
|
132
|
+
# Create the email message
|
|
133
|
+
message = EmailMessage()
|
|
134
|
+
message["From"] = sender_address
|
|
135
|
+
message["To"] = email_addresses
|
|
136
|
+
message["Subject"] = subject
|
|
137
|
+
message.set_content(body)
|
|
138
|
+
|
|
139
|
+
# Send the email
|
|
140
|
+
await aiosmtplib.send(
|
|
141
|
+
message,
|
|
142
|
+
hostname=server_host,
|
|
143
|
+
port=server_port,
|
|
144
|
+
username=username,
|
|
145
|
+
password=password,
|
|
146
|
+
use_tls=use_tls,
|
|
147
|
+
validate_certs=validate_certs,
|
|
148
|
+
start_tls=start_tls,
|
|
149
|
+
)
|
|
@@ -46,13 +46,13 @@ class SlackNotification(NotificationBase):
|
|
|
46
46
|
async def push(
|
|
47
47
|
self,
|
|
48
48
|
message: str,
|
|
49
|
-
severity: typing.
|
|
50
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
49
|
+
severity: typing.Optional[
|
|
50
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
51
51
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
52
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
53
|
-
custom_html: str = None,
|
|
54
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
55
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
52
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
53
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
54
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
55
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
56
56
|
):
|
|
57
57
|
webhook = self.params.get("webhook", None) or mlrun.get_secret_or_env(
|
|
58
58
|
"SLACK_WEBHOOK"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
import re
|
|
16
15
|
import typing
|
|
17
16
|
|
|
18
17
|
import aiohttp
|
|
@@ -38,13 +37,13 @@ class WebhookNotification(NotificationBase):
|
|
|
38
37
|
async def push(
|
|
39
38
|
self,
|
|
40
39
|
message: str,
|
|
41
|
-
severity: typing.
|
|
42
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
40
|
+
severity: typing.Optional[
|
|
41
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
43
42
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
44
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
45
|
-
custom_html: str = None,
|
|
46
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
47
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
43
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
44
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
45
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
46
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
48
47
|
):
|
|
49
48
|
url = self.params.get("url", None)
|
|
50
49
|
method = self.params.get("method", "post").lower()
|
|
@@ -94,6 +93,7 @@ class WebhookNotification(NotificationBase):
|
|
|
94
93
|
|
|
95
94
|
@staticmethod
|
|
96
95
|
def _serialize_runs_in_request_body(override_body, runs):
|
|
96
|
+
str_parsed_runs = ""
|
|
97
97
|
runs = runs or []
|
|
98
98
|
|
|
99
99
|
def parse_runs():
|
|
@@ -105,26 +105,22 @@ class WebhookNotification(NotificationBase):
|
|
|
105
105
|
parsed_run = {
|
|
106
106
|
"project": run["metadata"]["project"],
|
|
107
107
|
"name": run["metadata"]["name"],
|
|
108
|
+
"host": run["metadata"]["labels"]["host"],
|
|
108
109
|
"status": {"state": run["status"]["state"]},
|
|
109
110
|
}
|
|
110
|
-
if
|
|
111
|
-
parsed_run["
|
|
112
|
-
|
|
113
|
-
parsed_run["status"]["
|
|
114
|
-
elif results := run["status"].get("results"):
|
|
115
|
-
parsed_run["status"]["results"] = results
|
|
111
|
+
if run["status"].get("error", None):
|
|
112
|
+
parsed_run["status"]["error"] = run["status"]["error"]
|
|
113
|
+
elif run["status"].get("results", None):
|
|
114
|
+
parsed_run["status"]["results"] = run["status"]["results"]
|
|
116
115
|
parsed_runs.append(parsed_run)
|
|
117
116
|
return str(parsed_runs)
|
|
118
117
|
|
|
119
118
|
if isinstance(override_body, dict):
|
|
120
119
|
for key, value in override_body.items():
|
|
121
|
-
if
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
r"{{\s*runs\s*}}", str_parsed_runs, value
|
|
128
|
-
)
|
|
129
|
-
|
|
120
|
+
if "{{ runs }}" or "{{runs}}" in value:
|
|
121
|
+
if not str_parsed_runs:
|
|
122
|
+
str_parsed_runs = parse_runs()
|
|
123
|
+
override_body[key] = value.replace(
|
|
124
|
+
"{{ runs }}", str_parsed_runs
|
|
125
|
+
).replace("{{runs}}", str_parsed_runs)
|
|
130
126
|
return override_body
|
|
@@ -20,10 +20,6 @@ import traceback
|
|
|
20
20
|
import typing
|
|
21
21
|
from concurrent.futures import ThreadPoolExecutor
|
|
22
22
|
|
|
23
|
-
import mlrun_pipelines.common.ops
|
|
24
|
-
import mlrun_pipelines.models
|
|
25
|
-
import mlrun_pipelines.utils
|
|
26
|
-
|
|
27
23
|
import mlrun.common.constants as mlrun_constants
|
|
28
24
|
import mlrun.common.runtimes.constants
|
|
29
25
|
import mlrun.common.schemas
|
|
@@ -33,11 +29,14 @@ import mlrun.errors
|
|
|
33
29
|
import mlrun.lists
|
|
34
30
|
import mlrun.model
|
|
35
31
|
import mlrun.utils.helpers
|
|
32
|
+
import mlrun.utils.notifications.notification as notification_module
|
|
33
|
+
import mlrun.utils.notifications.notification.base as base
|
|
34
|
+
import mlrun_pipelines.common.ops
|
|
35
|
+
import mlrun_pipelines.models
|
|
36
|
+
import mlrun_pipelines.utils
|
|
36
37
|
from mlrun.utils import logger
|
|
37
38
|
from mlrun.utils.condition_evaluator import evaluate_condition_in_separate_process
|
|
38
39
|
|
|
39
|
-
from .notification import NotificationBase, NotificationTypes
|
|
40
|
-
|
|
41
40
|
|
|
42
41
|
class _NotificationPusherBase:
|
|
43
42
|
def _push(
|
|
@@ -100,13 +99,22 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
100
99
|
"aborted": "{resource} aborted",
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
def __init__(
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
runs: typing.Union[mlrun.lists.RunList, list],
|
|
105
|
+
default_params: typing.Optional[dict] = None,
|
|
106
|
+
):
|
|
104
107
|
self._runs = runs
|
|
108
|
+
self._default_params = default_params or {}
|
|
105
109
|
self._sync_notifications: list[
|
|
106
|
-
tuple[
|
|
110
|
+
tuple[
|
|
111
|
+
base.NotificationBase, mlrun.model.RunObject, mlrun.model.Notification
|
|
112
|
+
]
|
|
107
113
|
] = []
|
|
108
114
|
self._async_notifications: list[
|
|
109
|
-
tuple[
|
|
115
|
+
tuple[
|
|
116
|
+
base.NotificationBase, mlrun.model.RunObject, mlrun.model.Notification
|
|
117
|
+
]
|
|
110
118
|
] = []
|
|
111
119
|
|
|
112
120
|
for run in self._runs:
|
|
@@ -168,11 +176,6 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
168
176
|
logger.warning(
|
|
169
177
|
"Failed to push notification async",
|
|
170
178
|
error=mlrun.errors.err_to_str(result),
|
|
171
|
-
traceback=traceback.format_exception(
|
|
172
|
-
etype=type(result),
|
|
173
|
-
value=result,
|
|
174
|
-
tb=result.__traceback__,
|
|
175
|
-
),
|
|
176
179
|
)
|
|
177
180
|
|
|
178
181
|
logger.debug(
|
|
@@ -217,15 +220,18 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
217
220
|
|
|
218
221
|
def _load_notification(
|
|
219
222
|
self, run: mlrun.model.RunObject, notification_object: mlrun.model.Notification
|
|
220
|
-
) -> NotificationBase:
|
|
223
|
+
) -> base.NotificationBase:
|
|
221
224
|
name = notification_object.name
|
|
222
|
-
notification_type = NotificationTypes(
|
|
223
|
-
notification_object.kind or NotificationTypes.console
|
|
225
|
+
notification_type = notification_module.NotificationTypes(
|
|
226
|
+
notification_object.kind or notification_module.NotificationTypes.console
|
|
224
227
|
)
|
|
225
228
|
params = {}
|
|
226
229
|
params.update(notification_object.secret_params)
|
|
227
230
|
params.update(notification_object.params)
|
|
228
|
-
|
|
231
|
+
default_params = self._default_params.get(notification_type.value, {})
|
|
232
|
+
notification = notification_type.get_notification()(
|
|
233
|
+
name, params, default_params
|
|
234
|
+
)
|
|
229
235
|
if notification.is_async:
|
|
230
236
|
self._async_notifications.append((notification, run, notification_object))
|
|
231
237
|
else:
|
|
@@ -265,7 +271,7 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
265
271
|
|
|
266
272
|
def _push_notification_sync(
|
|
267
273
|
self,
|
|
268
|
-
notification: NotificationBase,
|
|
274
|
+
notification: base.NotificationBase,
|
|
269
275
|
run: mlrun.model.RunObject,
|
|
270
276
|
notification_object: mlrun.model.Notification,
|
|
271
277
|
):
|
|
@@ -313,7 +319,7 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
313
319
|
|
|
314
320
|
async def _push_notification_async(
|
|
315
321
|
self,
|
|
316
|
-
notification: NotificationBase,
|
|
322
|
+
notification: base.NotificationBase,
|
|
317
323
|
run: mlrun.model.RunObject,
|
|
318
324
|
notification_object: mlrun.model.Notification,
|
|
319
325
|
):
|
|
@@ -366,7 +372,7 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
366
372
|
run_uid: str,
|
|
367
373
|
project: str,
|
|
368
374
|
notification: mlrun.model.Notification,
|
|
369
|
-
status: str = None,
|
|
375
|
+
status: typing.Optional[str] = None,
|
|
370
376
|
sent_time: typing.Optional[datetime.datetime] = None,
|
|
371
377
|
reason: typing.Optional[str] = None,
|
|
372
378
|
):
|
|
@@ -521,9 +527,11 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
521
527
|
|
|
522
528
|
|
|
523
529
|
class CustomNotificationPusher(_NotificationPusherBase):
|
|
524
|
-
def __init__(self, notification_types: list[str] = None):
|
|
530
|
+
def __init__(self, notification_types: typing.Optional[list[str]] = None):
|
|
525
531
|
notifications = {
|
|
526
|
-
notification_type: NotificationTypes(
|
|
532
|
+
notification_type: notification_module.NotificationTypes(
|
|
533
|
+
notification_type
|
|
534
|
+
).get_notification()()
|
|
527
535
|
for notification_type in notification_types
|
|
528
536
|
}
|
|
529
537
|
self._sync_notifications = {
|
|
@@ -550,7 +558,7 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
550
558
|
mlrun.common.schemas.NotificationSeverity, str
|
|
551
559
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
552
560
|
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
553
|
-
custom_html: str = None,
|
|
561
|
+
custom_html: typing.Optional[str] = None,
|
|
554
562
|
):
|
|
555
563
|
def sync_push():
|
|
556
564
|
for notification_type, notification in self._sync_notifications.items():
|
|
@@ -572,14 +580,16 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
572
580
|
def add_notification(
|
|
573
581
|
self,
|
|
574
582
|
notification_type: str,
|
|
575
|
-
params: dict[str, str] = None,
|
|
583
|
+
params: typing.Optional[dict[str, str]] = None,
|
|
576
584
|
):
|
|
577
585
|
if notification_type in self._async_notifications:
|
|
578
586
|
self._async_notifications[notification_type].load_notification(params)
|
|
579
587
|
elif notification_type in self._sync_notifications:
|
|
580
588
|
self._sync_notifications[notification_type].load_notification(params)
|
|
581
589
|
else:
|
|
582
|
-
notification = NotificationTypes(
|
|
590
|
+
notification = notification_module.NotificationTypes(
|
|
591
|
+
notification_type
|
|
592
|
+
).get_notification()(
|
|
583
593
|
params=params,
|
|
584
594
|
)
|
|
585
595
|
if notification.is_async:
|
|
@@ -597,7 +607,9 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
597
607
|
else:
|
|
598
608
|
logger.warning(f"No notification of type {notification_type} in project")
|
|
599
609
|
|
|
600
|
-
def edit_notification(
|
|
610
|
+
def edit_notification(
|
|
611
|
+
self, notification_type: str, params: typing.Optional[dict[str, str]] = None
|
|
612
|
+
):
|
|
601
613
|
self.remove_notification(notification_type)
|
|
602
614
|
self.add_notification(notification_type, params)
|
|
603
615
|
|
|
@@ -611,7 +623,7 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
611
623
|
|
|
612
624
|
# get notification's inverse dependencies, and only push the notification if
|
|
613
625
|
# none of its inverse dependencies are being sent
|
|
614
|
-
inverse_dependencies = NotificationTypes(
|
|
626
|
+
inverse_dependencies = notification_module.NotificationTypes(
|
|
615
627
|
notification_type
|
|
616
628
|
).inverse_dependencies()
|
|
617
629
|
for inverse_dependency in inverse_dependencies:
|
|
@@ -627,8 +639,8 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
627
639
|
def push_pipeline_start_message(
|
|
628
640
|
self,
|
|
629
641
|
project: str,
|
|
630
|
-
commit_id: str = None,
|
|
631
|
-
pipeline_id: str = None,
|
|
642
|
+
commit_id: typing.Optional[str] = None,
|
|
643
|
+
pipeline_id: typing.Optional[str] = None,
|
|
632
644
|
has_workflow_url: bool = False,
|
|
633
645
|
):
|
|
634
646
|
message = f"Workflow started in project {project}"
|
|
@@ -656,7 +668,7 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
656
668
|
self,
|
|
657
669
|
runs: typing.Union[mlrun.lists.RunList, list],
|
|
658
670
|
push_all: bool = False,
|
|
659
|
-
state: str = None,
|
|
671
|
+
state: typing.Optional[str] = None,
|
|
660
672
|
):
|
|
661
673
|
"""
|
|
662
674
|
push a structured table with run results to notification targets
|
mlrun/utils/regex.py
CHANGED
|
@@ -86,7 +86,7 @@ tag_name = label_value
|
|
|
86
86
|
|
|
87
87
|
secret_key = k8s_secret_and_config_map_key
|
|
88
88
|
|
|
89
|
-
artifact_key = [r"[
|
|
89
|
+
artifact_key = [r"^[A-Za-z0-9]([-A-Za-z0-9_.]*[A-Za-z0-9])?$"]
|
|
90
90
|
|
|
91
91
|
# must not start with _
|
|
92
92
|
# must be alphanumeric or _
|
|
@@ -101,3 +101,5 @@ artifact_uri_pattern = r"^((?P<project>.*)/)?(?P<key>.*?)(\#(?P<iteration>.*?))?
|
|
|
101
101
|
artifact_producer_uri_pattern = (
|
|
102
102
|
r"^((?P<project>.*)/)?(?P<uid>.*?)(\-(?P<iteration>.*?))?$"
|
|
103
103
|
)
|
|
104
|
+
|
|
105
|
+
mail_regex = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mlrun
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0rc2
|
|
4
4
|
Summary: Tracking and config of machine learning runs
|
|
5
5
|
Home-page: https://github.com/mlrun/mlrun
|
|
6
6
|
Author: Yaron Haviv
|
|
@@ -23,35 +23,37 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: urllib3<1.27,>=1.26.9
|
|
25
25
|
Requires-Dist: GitPython>=3.1.41,~=3.1
|
|
26
|
-
Requires-Dist: aiohttp
|
|
26
|
+
Requires-Dist: aiohttp~=3.10.0
|
|
27
27
|
Requires-Dist: aiohttp-retry~=2.8.0
|
|
28
28
|
Requires-Dist: click~=8.1
|
|
29
29
|
Requires-Dist: nest-asyncio~=1.0
|
|
30
30
|
Requires-Dist: ipython~=8.10
|
|
31
|
-
Requires-Dist: nuclio-jupyter~=0.
|
|
32
|
-
Requires-Dist: numpy<1.27.0,>=1.
|
|
31
|
+
Requires-Dist: nuclio-jupyter~=0.11.1
|
|
32
|
+
Requires-Dist: numpy<1.27.0,>=1.26.4
|
|
33
33
|
Requires-Dist: pandas<2.2,>=1.2
|
|
34
34
|
Requires-Dist: pyarrow<18,>=10.0
|
|
35
35
|
Requires-Dist: pyyaml<7,>=5.4.1
|
|
36
36
|
Requires-Dist: requests~=2.32
|
|
37
37
|
Requires-Dist: tabulate~=0.8.6
|
|
38
38
|
Requires-Dist: v3io~=0.6.9
|
|
39
|
-
Requires-Dist: pydantic
|
|
39
|
+
Requires-Dist: pydantic>=1.10.15
|
|
40
40
|
Requires-Dist: mergedeep~=1.3
|
|
41
41
|
Requires-Dist: v3io-frames~=0.10.14
|
|
42
42
|
Requires-Dist: semver~=3.0
|
|
43
43
|
Requires-Dist: dependency-injector~=4.41
|
|
44
44
|
Requires-Dist: fsspec<2024.7,>=2023.9.2
|
|
45
45
|
Requires-Dist: v3iofs~=0.1.17
|
|
46
|
-
Requires-Dist: storey~=1.
|
|
46
|
+
Requires-Dist: storey~=1.8.0
|
|
47
47
|
Requires-Dist: inflection~=0.5.0
|
|
48
|
-
Requires-Dist: python-dotenv~=
|
|
49
|
-
Requires-Dist: setuptools
|
|
48
|
+
Requires-Dist: python-dotenv~=1.0
|
|
49
|
+
Requires-Dist: setuptools>=75.2
|
|
50
50
|
Requires-Dist: deprecated~=1.2
|
|
51
51
|
Requires-Dist: jinja2>=3.1.3,~=3.1
|
|
52
52
|
Requires-Dist: orjson<4,>=3.9.15
|
|
53
|
-
Requires-Dist: mlrun-pipelines-kfp-common~=0.
|
|
54
|
-
Requires-Dist: mlrun-pipelines-kfp-v1-8~=0.1
|
|
53
|
+
Requires-Dist: mlrun-pipelines-kfp-common~=0.2.2
|
|
54
|
+
Requires-Dist: mlrun-pipelines-kfp-v1-8~=0.2.1
|
|
55
|
+
Requires-Dist: docstring_parser~=0.16
|
|
56
|
+
Requires-Dist: aiosmtplib~=3.0
|
|
55
57
|
Provides-Extra: s3
|
|
56
58
|
Requires-Dist: boto3<1.36,>=1.28.0; extra == "s3"
|
|
57
59
|
Requires-Dist: aiobotocore<2.16,>=2.5.0; extra == "s3"
|
|
@@ -102,9 +104,9 @@ Requires-Dist: snowflake-connector-python~=3.7; extra == "snowflake"
|
|
|
102
104
|
Provides-Extra: api
|
|
103
105
|
Requires-Dist: uvicorn~=0.27.1; extra == "api"
|
|
104
106
|
Requires-Dist: dask-kubernetes~=0.11.0; extra == "api"
|
|
105
|
-
Requires-Dist: apscheduler
|
|
107
|
+
Requires-Dist: apscheduler<4,>=3.10.3; extra == "api"
|
|
106
108
|
Requires-Dist: objgraph~=3.6; extra == "api"
|
|
107
|
-
Requires-Dist: igz-mgmt~=0.
|
|
109
|
+
Requires-Dist: igz-mgmt~=0.4.1; extra == "api"
|
|
108
110
|
Requires-Dist: humanfriendly~=10.0; extra == "api"
|
|
109
111
|
Requires-Dist: fastapi~=0.110.0; extra == "api"
|
|
110
112
|
Requires-Dist: sqlalchemy~=1.4; extra == "api"
|
|
@@ -112,6 +114,7 @@ Requires-Dist: pymysql~=1.0; extra == "api"
|
|
|
112
114
|
Requires-Dist: alembic~=1.9; extra == "api"
|
|
113
115
|
Requires-Dist: timelength~=1.1; extra == "api"
|
|
114
116
|
Requires-Dist: memray~=1.12; sys_platform != "win32" and extra == "api"
|
|
117
|
+
Requires-Dist: aiosmtplib~=3.0; extra == "api"
|
|
115
118
|
Provides-Extra: all
|
|
116
119
|
Requires-Dist: adlfs==2023.9.0; extra == "all"
|
|
117
120
|
Requires-Dist: aiobotocore<2.16,>=2.5.0; extra == "all"
|
|
@@ -176,8 +179,9 @@ Requires-Dist: taoswswrap~=0.2.0; extra == "complete"
|
|
|
176
179
|
Provides-Extra: complete-api
|
|
177
180
|
Requires-Dist: adlfs==2023.9.0; extra == "complete-api"
|
|
178
181
|
Requires-Dist: aiobotocore<2.16,>=2.5.0; extra == "complete-api"
|
|
182
|
+
Requires-Dist: aiosmtplib~=3.0; extra == "complete-api"
|
|
179
183
|
Requires-Dist: alembic~=1.9; extra == "complete-api"
|
|
180
|
-
Requires-Dist: apscheduler
|
|
184
|
+
Requires-Dist: apscheduler<4,>=3.10.3; extra == "complete-api"
|
|
181
185
|
Requires-Dist: avro~=1.11; extra == "complete-api"
|
|
182
186
|
Requires-Dist: azure-core~=1.24; extra == "complete-api"
|
|
183
187
|
Requires-Dist: azure-identity~=1.5; extra == "complete-api"
|
|
@@ -195,7 +199,7 @@ Requires-Dist: google-cloud-storage==2.14.0; extra == "complete-api"
|
|
|
195
199
|
Requires-Dist: google-cloud==0.34; extra == "complete-api"
|
|
196
200
|
Requires-Dist: graphviz~=0.20.0; extra == "complete-api"
|
|
197
201
|
Requires-Dist: humanfriendly~=10.0; extra == "complete-api"
|
|
198
|
-
Requires-Dist: igz-mgmt~=0.
|
|
202
|
+
Requires-Dist: igz-mgmt~=0.4.1; extra == "complete-api"
|
|
199
203
|
Requires-Dist: kafka-python~=2.0; extra == "complete-api"
|
|
200
204
|
Requires-Dist: memray~=1.12; sys_platform != "win32" and extra == "complete-api"
|
|
201
205
|
Requires-Dist: mlflow~=2.8; extra == "complete-api"
|