mlrun 1.7.1rc10__py3-none-any.whl → 1.8.0rc8__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 +23 -21
- mlrun/__main__.py +3 -3
- mlrun/alerts/alert.py +148 -14
- mlrun/artifacts/__init__.py +1 -2
- mlrun/artifacts/base.py +46 -12
- mlrun/artifacts/dataset.py +16 -16
- mlrun/artifacts/document.py +334 -0
- mlrun/artifacts/manager.py +15 -13
- mlrun/artifacts/model.py +66 -53
- mlrun/common/constants.py +7 -0
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/feature_set.py +1 -0
- mlrun/common/formatters/function.py +1 -0
- mlrun/{model_monitoring/db/stores/base/__init__.py → common/formatters/model_endpoint.py} +16 -1
- mlrun/common/formatters/pipeline.py +1 -2
- mlrun/common/formatters/project.py +9 -0
- mlrun/common/model_monitoring/__init__.py +0 -5
- mlrun/common/model_monitoring/helpers.py +1 -29
- mlrun/common/runtimes/constants.py +1 -2
- mlrun/common/schemas/__init__.py +6 -2
- mlrun/common/schemas/alert.py +111 -19
- mlrun/common/schemas/api_gateway.py +3 -3
- mlrun/common/schemas/artifact.py +11 -7
- mlrun/common/schemas/auth.py +6 -4
- mlrun/common/schemas/background_task.py +7 -7
- mlrun/common/schemas/client_spec.py +2 -3
- mlrun/common/schemas/clusterization_spec.py +2 -2
- mlrun/common/schemas/common.py +53 -3
- mlrun/common/schemas/constants.py +15 -0
- 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 +2 -1
- mlrun/common/schemas/model_monitoring/constants.py +66 -14
- mlrun/common/schemas/model_monitoring/grafana.py +1 -1
- mlrun/common/schemas/model_monitoring/model_endpoints.py +91 -147
- mlrun/common/schemas/notification.py +24 -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 +25 -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 +67 -10
- mlrun/data_types/__init__.py +0 -2
- mlrun/data_types/infer.py +3 -1
- mlrun/data_types/spark.py +2 -1
- 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 +79 -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 +52 -51
- mlrun/datastore/store_resources.py +0 -2
- mlrun/datastore/targets.py +21 -21
- mlrun/datastore/utils.py +2 -2
- mlrun/datastore/v3io.py +4 -1
- mlrun/datastore/vectorstore.py +194 -0
- mlrun/datastore/wasbfs/fs.py +13 -12
- mlrun/db/base.py +208 -82
- mlrun/db/factory.py +0 -3
- mlrun/db/httpdb.py +1237 -386
- mlrun/db/nopdb.py +201 -74
- mlrun/errors.py +2 -2
- mlrun/execution.py +136 -50
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +41 -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 +117 -46
- mlrun/model_monitoring/__init__.py +4 -4
- mlrun/model_monitoring/api.py +61 -59
- mlrun/model_monitoring/applications/_application_steps.py +17 -17
- mlrun/model_monitoring/applications/base.py +165 -6
- mlrun/model_monitoring/applications/context.py +88 -37
- mlrun/model_monitoring/applications/evidently_base.py +0 -1
- mlrun/model_monitoring/applications/histogram_data_drift.py +43 -21
- mlrun/model_monitoring/applications/results.py +55 -3
- mlrun/model_monitoring/controller.py +207 -239
- mlrun/model_monitoring/db/__init__.py +0 -2
- mlrun/model_monitoring/db/_schedules.py +156 -0
- mlrun/model_monitoring/db/_stats.py +189 -0
- mlrun/model_monitoring/db/tsdb/base.py +78 -25
- 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 +255 -29
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +78 -17
- mlrun/model_monitoring/helpers.py +152 -49
- mlrun/model_monitoring/stream_processing.py +99 -283
- mlrun/model_monitoring/tracking_policy.py +10 -3
- mlrun/model_monitoring/writer.py +48 -36
- 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 +47 -16
- mlrun/platforms/iguazio.py +4 -1
- mlrun/projects/operations.py +27 -27
- mlrun/projects/pipelines.py +71 -36
- mlrun/projects/project.py +865 -206
- mlrun/run.py +53 -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 +19 -17
- mlrun/runtimes/nuclio/serving.py +18 -11
- 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 +185 -172
- mlrun/serving/server.py +7 -1
- mlrun/serving/states.py +97 -78
- mlrun/serving/utils.py +13 -2
- mlrun/serving/v1_serving.py +3 -2
- mlrun/serving/v2_serving.py +74 -65
- 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/clones.py +1 -1
- mlrun/utils/helpers.py +54 -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 +176 -0
- mlrun/utils/notifications/notification/slack.py +6 -6
- mlrun/utils/notifications/notification/webhook.py +6 -6
- mlrun/utils/notifications/notification_pusher.py +86 -44
- mlrun/utils/regex.py +3 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/METADATA +21 -16
- mlrun-1.8.0rc8.dist-info/RECORD +347 -0
- mlrun/model_monitoring/db/stores/__init__.py +0 -136
- mlrun/model_monitoring/db/stores/base/store.py +0 -213
- mlrun/model_monitoring/db/stores/sqldb/__init__.py +0 -13
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -71
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -190
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -103
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -40
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -659
- mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +0 -13
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -726
- mlrun/model_monitoring/model_endpoint.py +0 -118
- mlrun-1.7.1rc10.dist-info/RECORD +0 -351
- {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/LICENSE +0 -0
- {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/WHEEL +0 -0
- {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/top_level.txt +0 -0
mlrun/utils/helpers.py
CHANGED
|
@@ -40,7 +40,6 @@ import pandas
|
|
|
40
40
|
import semver
|
|
41
41
|
import yaml
|
|
42
42
|
from dateutil import parser
|
|
43
|
-
from mlrun_pipelines.models import PipelineRun
|
|
44
43
|
from pandas import Timedelta, Timestamp
|
|
45
44
|
from yaml.representer import RepresenterError
|
|
46
45
|
|
|
@@ -52,6 +51,7 @@ import mlrun.utils.regex
|
|
|
52
51
|
import mlrun.utils.version.version
|
|
53
52
|
from mlrun.common.constants import MYSQL_MEDIUMBLOB_SIZE_BYTES
|
|
54
53
|
from mlrun.config import config
|
|
54
|
+
from mlrun_pipelines.models import PipelineRun
|
|
55
55
|
|
|
56
56
|
from .logger import create_logger
|
|
57
57
|
from .retryer import ( # noqa: F401
|
|
@@ -167,6 +167,7 @@ class RunKeys:
|
|
|
167
167
|
inputs = "inputs"
|
|
168
168
|
returns = "returns"
|
|
169
169
|
artifacts = "artifacts"
|
|
170
|
+
artifact_uris = "artifact_uris"
|
|
170
171
|
outputs = "outputs"
|
|
171
172
|
data_stores = "data_stores"
|
|
172
173
|
secrets = "secret_sources"
|
|
@@ -220,7 +221,7 @@ def verify_field_regex(
|
|
|
220
221
|
|
|
221
222
|
|
|
222
223
|
def validate_builder_source(
|
|
223
|
-
source: str, pull_at_runtime: bool = False, workdir: str = None
|
|
224
|
+
source: str, pull_at_runtime: bool = False, workdir: Optional[str] = None
|
|
224
225
|
):
|
|
225
226
|
if pull_at_runtime or not source:
|
|
226
227
|
return
|
|
@@ -268,12 +269,14 @@ def validate_tag_name(
|
|
|
268
269
|
def validate_artifact_key_name(
|
|
269
270
|
artifact_key: str, field_name: str, raise_on_failure: bool = True
|
|
270
271
|
) -> bool:
|
|
272
|
+
field_type = "key" if field_name == "artifact.key" else "db_key"
|
|
271
273
|
return mlrun.utils.helpers.verify_field_regex(
|
|
272
274
|
field_name,
|
|
273
275
|
artifact_key,
|
|
274
276
|
mlrun.utils.regex.artifact_key,
|
|
275
277
|
raise_on_failure=raise_on_failure,
|
|
276
|
-
log_message="
|
|
278
|
+
log_message=f"Artifact {field_type} must start and end with an alphanumeric character, and may only contain "
|
|
279
|
+
"letters, numbers, hyphens, underscores, and dots.",
|
|
277
280
|
)
|
|
278
281
|
|
|
279
282
|
|
|
@@ -354,8 +357,8 @@ def verify_field_list_of_type(
|
|
|
354
357
|
def verify_dict_items_type(
|
|
355
358
|
name: str,
|
|
356
359
|
dictionary: dict,
|
|
357
|
-
expected_keys_types: list = None,
|
|
358
|
-
expected_values_types: list = None,
|
|
360
|
+
expected_keys_types: Optional[list] = None,
|
|
361
|
+
expected_values_types: Optional[list] = None,
|
|
359
362
|
):
|
|
360
363
|
if dictionary:
|
|
361
364
|
if not isinstance(dictionary, dict):
|
|
@@ -372,7 +375,7 @@ def verify_dict_items_type(
|
|
|
372
375
|
) from exc
|
|
373
376
|
|
|
374
377
|
|
|
375
|
-
def verify_list_items_type(list_, expected_types: list = None):
|
|
378
|
+
def verify_list_items_type(list_, expected_types: Optional[list] = None):
|
|
376
379
|
if list_ and expected_types:
|
|
377
380
|
list_items_types = set(map(type, list_))
|
|
378
381
|
expected_types = set(expected_types)
|
|
@@ -396,6 +399,10 @@ def now_date(tz: timezone = timezone.utc) -> datetime:
|
|
|
396
399
|
return datetime.now(tz=tz)
|
|
397
400
|
|
|
398
401
|
|
|
402
|
+
def datetime_min(tz: timezone = timezone.utc) -> datetime:
|
|
403
|
+
return datetime(1970, 1, 1, tzinfo=tz)
|
|
404
|
+
|
|
405
|
+
|
|
399
406
|
datetime_now = now_date
|
|
400
407
|
|
|
401
408
|
|
|
@@ -816,7 +823,9 @@ def _convert_python_package_version_to_image_tag(version: typing.Optional[str]):
|
|
|
816
823
|
|
|
817
824
|
|
|
818
825
|
def enrich_image_url(
|
|
819
|
-
image_url: str,
|
|
826
|
+
image_url: str,
|
|
827
|
+
client_version: Optional[str] = None,
|
|
828
|
+
client_python_version: Optional[str] = None,
|
|
820
829
|
) -> str:
|
|
821
830
|
client_version = _convert_python_package_version_to_image_tag(client_version)
|
|
822
831
|
server_version = _convert_python_package_version_to_image_tag(
|
|
@@ -856,7 +865,7 @@ def enrich_image_url(
|
|
|
856
865
|
|
|
857
866
|
|
|
858
867
|
def resolve_image_tag_suffix(
|
|
859
|
-
mlrun_version: str = None, python_version: str = None
|
|
868
|
+
mlrun_version: Optional[str] = None, python_version: Optional[str] = None
|
|
860
869
|
) -> str:
|
|
861
870
|
"""
|
|
862
871
|
resolves what suffix should be appended to the image tag
|
|
@@ -1175,7 +1184,7 @@ def get_function(function, namespaces, reload_modules: bool = False):
|
|
|
1175
1184
|
def get_handler_extended(
|
|
1176
1185
|
handler_path: str,
|
|
1177
1186
|
context=None,
|
|
1178
|
-
class_args: dict = None,
|
|
1187
|
+
class_args: Optional[dict] = None,
|
|
1179
1188
|
namespaces=None,
|
|
1180
1189
|
reload_modules: bool = False,
|
|
1181
1190
|
):
|
|
@@ -1694,17 +1703,22 @@ def merge_dicts_with_precedence(*dicts: dict) -> dict:
|
|
|
1694
1703
|
|
|
1695
1704
|
|
|
1696
1705
|
def validate_component_version_compatibility(
|
|
1697
|
-
component_name: typing.Literal["iguazio", "nuclio"],
|
|
1706
|
+
component_name: typing.Literal["iguazio", "nuclio", "mlrun-client"],
|
|
1707
|
+
*min_versions: str,
|
|
1708
|
+
mlrun_client_version: Optional[str] = None,
|
|
1698
1709
|
):
|
|
1699
1710
|
"""
|
|
1700
1711
|
:param component_name: Name of the component to validate compatibility for.
|
|
1701
1712
|
:param min_versions: Valid minimum version(s) required, assuming no 2 versions has equal major and minor.
|
|
1713
|
+
:param mlrun_client_version: Client version to validate when component_name is "mlrun-client".
|
|
1702
1714
|
"""
|
|
1703
1715
|
parsed_min_versions = [
|
|
1704
1716
|
semver.VersionInfo.parse(min_version) for min_version in min_versions
|
|
1705
1717
|
]
|
|
1706
1718
|
parsed_current_version = None
|
|
1707
1719
|
component_current_version = None
|
|
1720
|
+
# For mlrun client we don't assume compatability if we fail to parse the client version
|
|
1721
|
+
assume_compatible = component_name not in ["mlrun-client"]
|
|
1708
1722
|
try:
|
|
1709
1723
|
if component_name == "iguazio":
|
|
1710
1724
|
component_current_version = mlrun.mlconf.igz_version
|
|
@@ -1721,18 +1735,29 @@ def validate_component_version_compatibility(
|
|
|
1721
1735
|
parsed_current_version = semver.VersionInfo.parse(
|
|
1722
1736
|
mlrun.mlconf.nuclio_version
|
|
1723
1737
|
)
|
|
1738
|
+
if component_name == "mlrun-client":
|
|
1739
|
+
# dev version, assume compatible
|
|
1740
|
+
if mlrun_client_version and (
|
|
1741
|
+
mlrun_client_version.startswith("0.0.0+")
|
|
1742
|
+
or "unstable" in mlrun_client_version
|
|
1743
|
+
):
|
|
1744
|
+
return True
|
|
1745
|
+
|
|
1746
|
+
component_current_version = mlrun_client_version
|
|
1747
|
+
parsed_current_version = semver.Version.parse(mlrun_client_version)
|
|
1724
1748
|
if not parsed_current_version:
|
|
1725
|
-
return
|
|
1749
|
+
return assume_compatible
|
|
1726
1750
|
except ValueError:
|
|
1727
1751
|
# only log when version is set but invalid
|
|
1728
1752
|
if component_current_version:
|
|
1729
1753
|
logger.warning(
|
|
1730
|
-
"Unable to parse current version
|
|
1754
|
+
"Unable to parse current version",
|
|
1731
1755
|
component_name=component_name,
|
|
1732
1756
|
current_version=component_current_version,
|
|
1733
1757
|
min_versions=min_versions,
|
|
1758
|
+
assume_compatible=assume_compatible,
|
|
1734
1759
|
)
|
|
1735
|
-
return
|
|
1760
|
+
return assume_compatible
|
|
1736
1761
|
|
|
1737
1762
|
# Feature might have been back-ported e.g. nuclio node selection is supported from
|
|
1738
1763
|
# 1.5.20 and 1.6.10 but not in 1.6.9 - therefore we reverse sort to validate against 1.6.x 1st and
|
|
@@ -1797,9 +1822,8 @@ def _reload(module, max_recursion_depth):
|
|
|
1797
1822
|
def run_with_retry(
|
|
1798
1823
|
retry_count: int,
|
|
1799
1824
|
func: typing.Callable,
|
|
1800
|
-
retry_on_exceptions:
|
|
1801
|
-
type[Exception],
|
|
1802
|
-
tuple[type[Exception]],
|
|
1825
|
+
retry_on_exceptions: Optional[
|
|
1826
|
+
typing.Union[type[Exception], tuple[type[Exception]]]
|
|
1803
1827
|
] = None,
|
|
1804
1828
|
*args,
|
|
1805
1829
|
**kwargs,
|
|
@@ -1832,3 +1856,17 @@ def run_with_retry(
|
|
|
1832
1856
|
if attempt == retry_count:
|
|
1833
1857
|
raise
|
|
1834
1858
|
raise last_exception
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
def join_urls(base_url: Optional[str], path: Optional[str]) -> str:
|
|
1862
|
+
"""
|
|
1863
|
+
Joins a base URL with a path, ensuring proper handling of slashes.
|
|
1864
|
+
|
|
1865
|
+
:param base_url: The base URL (e.g., "http://example.com").
|
|
1866
|
+
:param path: The path to append to the base URL (e.g., "/path/to/resource").
|
|
1867
|
+
|
|
1868
|
+
:return: A unified URL with exactly one slash between base_url and path.
|
|
1869
|
+
"""
|
|
1870
|
+
if base_url is None:
|
|
1871
|
+
base_url = ""
|
|
1872
|
+
return f"{base_url.rstrip('/')}/{path.lstrip('/')}" if path else base_url
|
mlrun/utils/logger.py
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
|
|
14
|
+
import datetime
|
|
15
15
|
import logging
|
|
16
16
|
import os
|
|
17
|
+
import string
|
|
18
|
+
import sys
|
|
17
19
|
import typing
|
|
18
20
|
from enum import Enum
|
|
19
21
|
from functools import cached_property
|
|
@@ -22,15 +24,16 @@ from traceback import format_exception
|
|
|
22
24
|
from typing import IO, Optional, Union
|
|
23
25
|
|
|
24
26
|
import orjson
|
|
25
|
-
import pydantic
|
|
27
|
+
import pydantic.v1
|
|
26
28
|
|
|
29
|
+
from mlrun import errors
|
|
27
30
|
from mlrun.config import config
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
class _BaseFormatter(logging.Formatter):
|
|
31
34
|
def _json_dump(self, json_object):
|
|
32
35
|
def default(obj):
|
|
33
|
-
if isinstance(obj, pydantic.BaseModel):
|
|
36
|
+
if isinstance(obj, pydantic.v1.BaseModel):
|
|
34
37
|
return obj.dict()
|
|
35
38
|
|
|
36
39
|
# EAFP all the way.
|
|
@@ -93,6 +96,98 @@ class HumanReadableFormatter(_BaseFormatter):
|
|
|
93
96
|
return record_with
|
|
94
97
|
|
|
95
98
|
|
|
99
|
+
class CustomFormatter(HumanReadableFormatter):
|
|
100
|
+
"""
|
|
101
|
+
To enable custom logger formatter, configure MLRun with the following env variables:
|
|
102
|
+
1. "MLRUN_LOG_FORMATTER" = "custom" - change the default log formatter.
|
|
103
|
+
2. "MLRUN_LOG_FORMAT_OVERRIDE" = "> {timestamp} [{level}] Running module: {module} {message} {more}" - logger format
|
|
104
|
+
* Note that your custom format must include those 4 fields - timestamp, level, message and more
|
|
105
|
+
If the custom format is not configured properly , MLRun will use the default logger (human format).
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
# This attribute is used to solve an issue
|
|
109
|
+
# that causes the warning to be written numerous times(for any log generation).
|
|
110
|
+
# We want to print the errors just once, not for each logger generation.
|
|
111
|
+
fail_on_format_configuration = False # for issues that relates to unrecognized keys
|
|
112
|
+
fail_on_missing_default_keys_key = (
|
|
113
|
+
False # for issues that relates to missing default keys
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def format(self, record) -> str:
|
|
117
|
+
more = self._resolve_more(record)
|
|
118
|
+
custom_format = config.log_format_override
|
|
119
|
+
_custom_format = None
|
|
120
|
+
current_time = datetime.datetime.now()
|
|
121
|
+
formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S,%f")[:-3]
|
|
122
|
+
try:
|
|
123
|
+
if custom_format:
|
|
124
|
+
default_keys = ["timestamp", "level", "message", "more"]
|
|
125
|
+
formatter = string.Formatter()
|
|
126
|
+
custom_format_keys = [
|
|
127
|
+
key
|
|
128
|
+
for _, key, _, _ in formatter.parse(custom_format)
|
|
129
|
+
if key is not None
|
|
130
|
+
]
|
|
131
|
+
missing_default_flags = list(
|
|
132
|
+
set(default_keys) - set(custom_format_keys)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if (
|
|
136
|
+
missing_default_flags
|
|
137
|
+
and not CustomFormatter.fail_on_missing_default_keys_key
|
|
138
|
+
):
|
|
139
|
+
print(
|
|
140
|
+
f'> {formatted_time} [warning] Custom loggers must '
|
|
141
|
+
f'include those keys within the logger format, {", ".join(default_keys)} '
|
|
142
|
+
f'your format is missing: {", ".join(missing_default_flags)}',
|
|
143
|
+
file=sys.stderr,
|
|
144
|
+
)
|
|
145
|
+
CustomFormatter.fail_on_missing_default_keys_key = True
|
|
146
|
+
record_dict = record.__dict__
|
|
147
|
+
missing_format_configuraiton_keys = list(
|
|
148
|
+
set(custom_format_keys)
|
|
149
|
+
- set(default_keys)
|
|
150
|
+
- set(record_dict.keys())
|
|
151
|
+
)
|
|
152
|
+
if missing_format_configuraiton_keys:
|
|
153
|
+
if not CustomFormatter.fail_on_format_configuration:
|
|
154
|
+
print(
|
|
155
|
+
f"> {formatted_time} [warning] Failed to create custom logger due "
|
|
156
|
+
f'to missing format key in the log record: {", ".join(missing_format_configuraiton_keys)}',
|
|
157
|
+
file=sys.stderr,
|
|
158
|
+
)
|
|
159
|
+
CustomFormatter.fail_on_format_configuration = True
|
|
160
|
+
_format = (
|
|
161
|
+
f"> {self.formatTime(record, self.datefmt)} "
|
|
162
|
+
f"[{record.levelname.lower()}] "
|
|
163
|
+
f"{record.getMessage().rstrip()}"
|
|
164
|
+
f"{more}"
|
|
165
|
+
)
|
|
166
|
+
_custom_format = custom_format.format(
|
|
167
|
+
timestamp=self.formatTime(record, self.datefmt),
|
|
168
|
+
level=record.levelname.lower(),
|
|
169
|
+
message=record.getMessage().rstrip(),
|
|
170
|
+
more=more or "",
|
|
171
|
+
**record_dict,
|
|
172
|
+
)
|
|
173
|
+
CustomFormatter.fail_on_format_configuration = True
|
|
174
|
+
except Exception as e:
|
|
175
|
+
if not CustomFormatter.fail_on_format_configuration:
|
|
176
|
+
print(
|
|
177
|
+
f"> {formatted_time} [warning] Failed to create custom logger, "
|
|
178
|
+
f"see Exception: {errors.err_to_str(e)}",
|
|
179
|
+
file=sys.stderr,
|
|
180
|
+
)
|
|
181
|
+
CustomFormatter.fail_on_format_configuration = True
|
|
182
|
+
_format = _custom_format or (
|
|
183
|
+
f"> {self.formatTime(record, self.datefmt)} "
|
|
184
|
+
f"[{record.levelname.lower()}] "
|
|
185
|
+
f"{record.getMessage().rstrip()}"
|
|
186
|
+
f"{more}"
|
|
187
|
+
)
|
|
188
|
+
return _format
|
|
189
|
+
|
|
190
|
+
|
|
96
191
|
class HumanReadableExtendedFormatter(HumanReadableFormatter):
|
|
97
192
|
_colors = {
|
|
98
193
|
logging.NOTSET: "",
|
|
@@ -272,17 +367,24 @@ class FormatterKinds(Enum):
|
|
|
272
367
|
HUMAN = "human"
|
|
273
368
|
HUMAN_EXTENDED = "human_extended"
|
|
274
369
|
JSON = "json"
|
|
370
|
+
CUSTOM = "custom"
|
|
275
371
|
|
|
276
372
|
|
|
277
373
|
def resolve_formatter_by_kind(
|
|
278
374
|
formatter_kind: FormatterKinds,
|
|
279
375
|
) -> type[
|
|
280
|
-
typing.Union[
|
|
376
|
+
typing.Union[
|
|
377
|
+
HumanReadableFormatter,
|
|
378
|
+
HumanReadableExtendedFormatter,
|
|
379
|
+
JSONFormatter,
|
|
380
|
+
CustomFormatter,
|
|
381
|
+
]
|
|
281
382
|
]:
|
|
282
383
|
return {
|
|
283
384
|
FormatterKinds.HUMAN: HumanReadableFormatter,
|
|
284
385
|
FormatterKinds.HUMAN_EXTENDED: HumanReadableExtendedFormatter,
|
|
285
386
|
FormatterKinds.JSON: JSONFormatter,
|
|
387
|
+
FormatterKinds.CUSTOM: CustomFormatter,
|
|
286
388
|
}[formatter_kind]
|
|
287
389
|
|
|
288
390
|
|
|
@@ -14,30 +14,32 @@
|
|
|
14
14
|
|
|
15
15
|
import enum
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
import mlrun.common.schemas.notification as notifications
|
|
18
|
+
import mlrun.utils.notifications.notification.base as base
|
|
19
|
+
import mlrun.utils.notifications.notification.console as console
|
|
20
|
+
import mlrun.utils.notifications.notification.git as git
|
|
21
|
+
import mlrun.utils.notifications.notification.ipython as ipython
|
|
22
|
+
import mlrun.utils.notifications.notification.mail as mail
|
|
23
|
+
import mlrun.utils.notifications.notification.slack as slack
|
|
24
|
+
import mlrun.utils.notifications.notification.webhook as webhook
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class NotificationTypes(str, enum.Enum):
|
|
28
|
-
console = NotificationKind.console.value
|
|
29
|
-
git = NotificationKind.git.value
|
|
30
|
-
ipython = NotificationKind.ipython.value
|
|
31
|
-
slack = NotificationKind.slack.value
|
|
32
|
-
|
|
28
|
+
console = notifications.NotificationKind.console.value
|
|
29
|
+
git = notifications.NotificationKind.git.value
|
|
30
|
+
ipython = notifications.NotificationKind.ipython.value
|
|
31
|
+
slack = notifications.NotificationKind.slack.value
|
|
32
|
+
mail = notifications.NotificationKind.mail.value
|
|
33
|
+
webhook = notifications.NotificationKind.webhook.value
|
|
33
34
|
|
|
34
|
-
def get_notification(self) -> type[NotificationBase]:
|
|
35
|
+
def get_notification(self) -> type[base.NotificationBase]:
|
|
35
36
|
return {
|
|
36
|
-
self.console: ConsoleNotification,
|
|
37
|
-
self.git: GitNotification,
|
|
38
|
-
self.ipython: IPythonNotification,
|
|
39
|
-
self.slack: SlackNotification,
|
|
40
|
-
self.
|
|
37
|
+
self.console: console.ConsoleNotification,
|
|
38
|
+
self.git: git.GitNotification,
|
|
39
|
+
self.ipython: ipython.IPythonNotification,
|
|
40
|
+
self.slack: slack.SlackNotification,
|
|
41
|
+
self.mail: mail.MailNotification,
|
|
42
|
+
self.webhook: webhook.WebhookNotification,
|
|
41
43
|
}.get(self)
|
|
42
44
|
|
|
43
45
|
def inverse_dependencies(self) -> list[str]:
|
|
@@ -64,5 +66,6 @@ class NotificationTypes(str, enum.Enum):
|
|
|
64
66
|
cls.git,
|
|
65
67
|
cls.ipython,
|
|
66
68
|
cls.slack,
|
|
69
|
+
cls.mail,
|
|
67
70
|
cls.webhook,
|
|
68
71
|
]
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import asyncio
|
|
16
16
|
import typing
|
|
17
|
+
from copy import deepcopy
|
|
17
18
|
|
|
18
19
|
import mlrun.common.schemas
|
|
19
20
|
import mlrun.lists
|
|
@@ -22,11 +23,20 @@ import mlrun.lists
|
|
|
22
23
|
class NotificationBase:
|
|
23
24
|
def __init__(
|
|
24
25
|
self,
|
|
25
|
-
name: str = None,
|
|
26
|
-
params: dict[str, str] = None,
|
|
26
|
+
name: typing.Optional[str] = None,
|
|
27
|
+
params: typing.Optional[dict[str, str]] = None,
|
|
28
|
+
default_params: typing.Optional[dict[str, str]] = None,
|
|
27
29
|
):
|
|
30
|
+
"""
|
|
31
|
+
NotificationBase is the base class for all notification types.
|
|
32
|
+
|
|
33
|
+
:param name: The name of the notification.
|
|
34
|
+
:param params: The parameters of the notification.
|
|
35
|
+
:param default_params: The default parameters of the notification. Used for server-side enrichment purposes.
|
|
36
|
+
"""
|
|
28
37
|
self.name = name
|
|
29
38
|
self.params = params or {}
|
|
39
|
+
self.params = self.enrich_default_params(self.params, default_params)
|
|
30
40
|
|
|
31
41
|
@classmethod
|
|
32
42
|
def validate_params(cls, params):
|
|
@@ -43,13 +53,13 @@ class NotificationBase:
|
|
|
43
53
|
def push(
|
|
44
54
|
self,
|
|
45
55
|
message: str,
|
|
46
|
-
severity: typing.
|
|
47
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
56
|
+
severity: typing.Optional[
|
|
57
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
48
58
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
49
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
50
|
-
custom_html: str = None,
|
|
51
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
52
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
59
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
60
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
61
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
62
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
53
63
|
):
|
|
54
64
|
raise NotImplementedError()
|
|
55
65
|
|
|
@@ -59,16 +69,25 @@ class NotificationBase:
|
|
|
59
69
|
) -> None:
|
|
60
70
|
self.params = params or {}
|
|
61
71
|
|
|
72
|
+
@classmethod
|
|
73
|
+
def enrich_default_params(
|
|
74
|
+
cls, params: dict, default_params: typing.Optional[dict] = None
|
|
75
|
+
) -> dict:
|
|
76
|
+
default_params = default_params or {}
|
|
77
|
+
returned_params = deepcopy(default_params)
|
|
78
|
+
returned_params.update(params)
|
|
79
|
+
return returned_params
|
|
80
|
+
|
|
62
81
|
def _get_html(
|
|
63
82
|
self,
|
|
64
83
|
message: str,
|
|
65
|
-
severity: typing.
|
|
66
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
84
|
+
severity: typing.Optional[
|
|
85
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
67
86
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
68
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
69
|
-
custom_html: str = None,
|
|
70
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
71
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
87
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
88
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
89
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
90
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
72
91
|
) -> str:
|
|
73
92
|
if custom_html:
|
|
74
93
|
return custom_html
|
|
@@ -31,13 +31,13 @@ class ConsoleNotification(NotificationBase):
|
|
|
31
31
|
def push(
|
|
32
32
|
self,
|
|
33
33
|
message: str,
|
|
34
|
-
severity: typing.
|
|
35
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
34
|
+
severity: typing.Optional[
|
|
35
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
36
36
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
37
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
38
|
-
custom_html: str = None,
|
|
39
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
40
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
37
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
38
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
39
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
40
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
41
41
|
):
|
|
42
42
|
severity = self._resolve_severity(severity)
|
|
43
43
|
print(f"[{severity}] {message}")
|
|
@@ -54,13 +54,13 @@ class GitNotification(NotificationBase):
|
|
|
54
54
|
async def push(
|
|
55
55
|
self,
|
|
56
56
|
message: str,
|
|
57
|
-
severity: typing.
|
|
58
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
57
|
+
severity: typing.Optional[
|
|
58
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
59
59
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
60
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
61
|
-
custom_html: str = None,
|
|
62
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
63
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
60
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
61
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
62
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
63
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
64
64
|
):
|
|
65
65
|
git_repo = self.params.get("repo", None)
|
|
66
66
|
git_issue = self.params.get("issue", None)
|
|
@@ -85,11 +85,11 @@ class GitNotification(NotificationBase):
|
|
|
85
85
|
@staticmethod
|
|
86
86
|
async def _pr_comment(
|
|
87
87
|
message: str,
|
|
88
|
-
repo: str = None,
|
|
89
|
-
issue: int = None,
|
|
90
|
-
merge_request: int = None,
|
|
91
|
-
token: str = None,
|
|
92
|
-
server: str = None,
|
|
88
|
+
repo: typing.Optional[str] = None,
|
|
89
|
+
issue: typing.Optional[int] = None,
|
|
90
|
+
merge_request: typing.Optional[int] = None,
|
|
91
|
+
token: typing.Optional[str] = None,
|
|
92
|
+
server: typing.Optional[str] = None,
|
|
93
93
|
gitlab: bool = False,
|
|
94
94
|
) -> str:
|
|
95
95
|
"""push comment message to Git system PR/issue
|
|
@@ -28,10 +28,11 @@ class IPythonNotification(NotificationBase):
|
|
|
28
28
|
|
|
29
29
|
def __init__(
|
|
30
30
|
self,
|
|
31
|
-
name: str = None,
|
|
32
|
-
params: dict[str, str] = None,
|
|
31
|
+
name: typing.Optional[str] = None,
|
|
32
|
+
params: typing.Optional[dict[str, str]] = None,
|
|
33
|
+
default_params: typing.Optional[dict[str, str]] = None,
|
|
33
34
|
):
|
|
34
|
-
super().__init__(name, params)
|
|
35
|
+
super().__init__(name, params, default_params)
|
|
35
36
|
self._ipython = None
|
|
36
37
|
try:
|
|
37
38
|
import IPython
|
|
@@ -48,13 +49,13 @@ class IPythonNotification(NotificationBase):
|
|
|
48
49
|
def push(
|
|
49
50
|
self,
|
|
50
51
|
message: str,
|
|
51
|
-
severity: typing.
|
|
52
|
-
mlrun.common.schemas.NotificationSeverity, str
|
|
52
|
+
severity: typing.Optional[
|
|
53
|
+
typing.Union[mlrun.common.schemas.NotificationSeverity, str]
|
|
53
54
|
] = mlrun.common.schemas.NotificationSeverity.INFO,
|
|
54
|
-
runs: typing.Union[mlrun.lists.RunList, list] = None,
|
|
55
|
-
custom_html: str = None,
|
|
56
|
-
alert: mlrun.common.schemas.AlertConfig = None,
|
|
57
|
-
event_data: mlrun.common.schemas.Event = None,
|
|
55
|
+
runs: typing.Optional[typing.Union[mlrun.lists.RunList, list]] = None,
|
|
56
|
+
custom_html: typing.Optional[typing.Optional[str]] = None,
|
|
57
|
+
alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
|
|
58
|
+
event_data: typing.Optional[mlrun.common.schemas.Event] = None,
|
|
58
59
|
):
|
|
59
60
|
if not self._ipython:
|
|
60
61
|
mlrun.utils.helpers.logger.debug(
|