mlrun 1.7.0rc14__py3-none-any.whl → 1.7.0rc16__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 +10 -1
- mlrun/__main__.py +18 -109
- mlrun/{runtimes/mpijob/v1alpha1.py → alerts/__init__.py} +2 -16
- mlrun/alerts/alert.py +141 -0
- mlrun/artifacts/__init__.py +8 -3
- mlrun/artifacts/base.py +36 -253
- mlrun/artifacts/dataset.py +9 -190
- mlrun/artifacts/manager.py +20 -41
- mlrun/artifacts/model.py +8 -140
- mlrun/artifacts/plots.py +14 -375
- mlrun/common/schemas/__init__.py +4 -2
- mlrun/common/schemas/alert.py +46 -4
- mlrun/common/schemas/api_gateway.py +4 -0
- mlrun/common/schemas/artifact.py +15 -0
- mlrun/common/schemas/auth.py +2 -0
- mlrun/common/schemas/model_monitoring/__init__.py +8 -1
- mlrun/common/schemas/model_monitoring/constants.py +40 -4
- mlrun/common/schemas/model_monitoring/model_endpoints.py +73 -2
- mlrun/common/schemas/project.py +2 -0
- mlrun/config.py +7 -4
- mlrun/data_types/to_pandas.py +4 -4
- mlrun/datastore/base.py +41 -9
- mlrun/datastore/datastore_profile.py +54 -4
- mlrun/datastore/inmem.py +2 -2
- mlrun/datastore/sources.py +43 -2
- mlrun/datastore/store_resources.py +2 -6
- mlrun/datastore/targets.py +106 -39
- mlrun/db/base.py +23 -3
- mlrun/db/httpdb.py +101 -47
- mlrun/db/nopdb.py +20 -2
- mlrun/errors.py +5 -0
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +12 -47
- mlrun/feature_store/feature_set.py +9 -0
- mlrun/feature_store/retrieval/base.py +9 -4
- mlrun/feature_store/retrieval/conversion.py +4 -4
- mlrun/feature_store/retrieval/dask_merger.py +2 -0
- mlrun/feature_store/retrieval/job.py +2 -0
- mlrun/feature_store/retrieval/local_merger.py +2 -0
- mlrun/feature_store/retrieval/spark_merger.py +5 -0
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +5 -10
- mlrun/launcher/base.py +4 -3
- mlrun/launcher/client.py +1 -1
- mlrun/lists.py +4 -2
- mlrun/model.py +25 -11
- mlrun/model_monitoring/__init__.py +1 -1
- mlrun/model_monitoring/api.py +41 -18
- mlrun/model_monitoring/application.py +5 -305
- mlrun/model_monitoring/applications/__init__.py +11 -0
- mlrun/model_monitoring/applications/_application_steps.py +157 -0
- mlrun/model_monitoring/applications/base.py +282 -0
- mlrun/model_monitoring/applications/context.py +214 -0
- mlrun/model_monitoring/applications/evidently_base.py +211 -0
- mlrun/model_monitoring/applications/histogram_data_drift.py +132 -91
- mlrun/model_monitoring/applications/results.py +99 -0
- mlrun/model_monitoring/controller.py +3 -1
- mlrun/model_monitoring/db/__init__.py +2 -0
- mlrun/model_monitoring/db/stores/base/store.py +9 -36
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +7 -6
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +63 -110
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +104 -187
- mlrun/model_monitoring/db/tsdb/__init__.py +71 -0
- mlrun/model_monitoring/db/tsdb/base.py +135 -0
- mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +117 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +404 -0
- mlrun/model_monitoring/db/v3io_tsdb_reader.py +134 -0
- mlrun/model_monitoring/evidently_application.py +6 -118
- mlrun/model_monitoring/helpers.py +1 -1
- mlrun/model_monitoring/model_endpoint.py +3 -2
- mlrun/model_monitoring/stream_processing.py +48 -213
- mlrun/model_monitoring/writer.py +101 -121
- mlrun/platforms/__init__.py +10 -9
- mlrun/platforms/iguazio.py +21 -202
- mlrun/projects/operations.py +11 -7
- mlrun/projects/pipelines.py +13 -76
- mlrun/projects/project.py +73 -45
- mlrun/render.py +11 -13
- mlrun/run.py +6 -41
- mlrun/runtimes/__init__.py +3 -3
- mlrun/runtimes/base.py +6 -6
- mlrun/runtimes/funcdoc.py +0 -28
- mlrun/runtimes/kubejob.py +2 -1
- mlrun/runtimes/local.py +1 -1
- mlrun/runtimes/mpijob/__init__.py +0 -20
- mlrun/runtimes/mpijob/v1.py +1 -1
- mlrun/runtimes/nuclio/api_gateway.py +75 -9
- mlrun/runtimes/nuclio/function.py +9 -35
- mlrun/runtimes/pod.py +16 -36
- mlrun/runtimes/remotesparkjob.py +1 -1
- mlrun/runtimes/sparkjob/spark3job.py +1 -1
- mlrun/runtimes/utils.py +1 -39
- mlrun/utils/helpers.py +72 -71
- mlrun/utils/notifications/notification/base.py +1 -1
- mlrun/utils/notifications/notification/slack.py +12 -5
- mlrun/utils/notifications/notification/webhook.py +1 -1
- mlrun/utils/notifications/notification_pusher.py +134 -14
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc14.dist-info → mlrun-1.7.0rc16.dist-info}/METADATA +4 -3
- {mlrun-1.7.0rc14.dist-info → mlrun-1.7.0rc16.dist-info}/RECORD +105 -95
- mlrun/kfpops.py +0 -865
- mlrun/platforms/other.py +0 -305
- /mlrun/{runtimes → common/runtimes}/constants.py +0 -0
- {mlrun-1.7.0rc14.dist-info → mlrun-1.7.0rc16.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc14.dist-info → mlrun-1.7.0rc16.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc14.dist-info → mlrun-1.7.0rc16.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc14.dist-info → mlrun-1.7.0rc16.dist-info}/top_level.txt +0 -0
mlrun/artifacts/model.py
CHANGED
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import tempfile
|
|
16
|
+
import warnings
|
|
16
17
|
from os import path
|
|
17
18
|
from typing import Any, Optional
|
|
18
19
|
|
|
19
20
|
import pandas as pd
|
|
20
21
|
import yaml
|
|
21
|
-
from deprecated import deprecated
|
|
22
22
|
|
|
23
23
|
import mlrun
|
|
24
24
|
import mlrun.datastore
|
|
@@ -27,7 +27,7 @@ from ..data_types import InferOptions, get_infer_interface
|
|
|
27
27
|
from ..features import Feature
|
|
28
28
|
from ..model import ObjectList
|
|
29
29
|
from ..utils import StorePrefix, is_relative_path
|
|
30
|
-
from .base import Artifact, ArtifactSpec,
|
|
30
|
+
from .base import Artifact, ArtifactSpec, upload_extra_data
|
|
31
31
|
|
|
32
32
|
model_spec_filename = "model_spec.yaml"
|
|
33
33
|
|
|
@@ -149,6 +149,12 @@ class ModelArtifact(Artifact):
|
|
|
149
149
|
model_dir=None,
|
|
150
150
|
**kwargs,
|
|
151
151
|
):
|
|
152
|
+
if key or body or format or target_path:
|
|
153
|
+
warnings.warn(
|
|
154
|
+
"Artifact constructor parameters are deprecated and will be removed in 1.9.0. "
|
|
155
|
+
"Use the metadata and spec parameters instead.",
|
|
156
|
+
DeprecationWarning,
|
|
157
|
+
)
|
|
152
158
|
super().__init__(key, body, format=format, target_path=target_path, **kwargs)
|
|
153
159
|
model_file = str(model_file or "")
|
|
154
160
|
if model_file and "/" in model_file:
|
|
@@ -397,144 +403,6 @@ class ModelArtifact(Artifact):
|
|
|
397
403
|
return mlrun.get_dataitem(target_model_path).get()
|
|
398
404
|
|
|
399
405
|
|
|
400
|
-
# TODO: remove in 1.7.0
|
|
401
|
-
@deprecated(
|
|
402
|
-
version="1.3.0",
|
|
403
|
-
reason="'LegacyModelArtifact' will be removed in 1.7.0, use 'ModelArtifact' instead",
|
|
404
|
-
category=FutureWarning,
|
|
405
|
-
)
|
|
406
|
-
class LegacyModelArtifact(LegacyArtifact):
|
|
407
|
-
"""ML Model artifact
|
|
408
|
-
|
|
409
|
-
Store link to ML model file(s) along with the model metrics, parameters, schema, and stats
|
|
410
|
-
"""
|
|
411
|
-
|
|
412
|
-
_dict_fields = LegacyArtifact._dict_fields + [
|
|
413
|
-
"model_file",
|
|
414
|
-
"metrics",
|
|
415
|
-
"parameters",
|
|
416
|
-
"inputs",
|
|
417
|
-
"outputs",
|
|
418
|
-
"framework",
|
|
419
|
-
"algorithm",
|
|
420
|
-
"extra_data",
|
|
421
|
-
"feature_vector",
|
|
422
|
-
"feature_weights",
|
|
423
|
-
"feature_stats",
|
|
424
|
-
"model_target_file",
|
|
425
|
-
]
|
|
426
|
-
kind = "model"
|
|
427
|
-
_store_prefix = StorePrefix.Model
|
|
428
|
-
|
|
429
|
-
def __init__(
|
|
430
|
-
self,
|
|
431
|
-
key=None,
|
|
432
|
-
body=None,
|
|
433
|
-
format=None,
|
|
434
|
-
model_file=None,
|
|
435
|
-
metrics=None,
|
|
436
|
-
target_path=None,
|
|
437
|
-
parameters=None,
|
|
438
|
-
inputs=None,
|
|
439
|
-
outputs=None,
|
|
440
|
-
framework=None,
|
|
441
|
-
algorithm=None,
|
|
442
|
-
feature_vector=None,
|
|
443
|
-
feature_weights=None,
|
|
444
|
-
extra_data=None,
|
|
445
|
-
model_target_file=None,
|
|
446
|
-
**kwargs,
|
|
447
|
-
):
|
|
448
|
-
super().__init__(key, body, format=format, target_path=target_path, **kwargs)
|
|
449
|
-
self._inputs: Optional[ObjectList] = None
|
|
450
|
-
self._outputs: Optional[ObjectList] = None
|
|
451
|
-
|
|
452
|
-
self.model_file = model_file
|
|
453
|
-
self.parameters = parameters or {}
|
|
454
|
-
self.metrics = metrics or {}
|
|
455
|
-
self.inputs = inputs or []
|
|
456
|
-
self.outputs = outputs or []
|
|
457
|
-
self.extra_data = extra_data or {}
|
|
458
|
-
self.framework = framework
|
|
459
|
-
self.algorithm = algorithm
|
|
460
|
-
self.feature_vector = feature_vector
|
|
461
|
-
self.feature_weights = feature_weights
|
|
462
|
-
self.feature_stats = None
|
|
463
|
-
self.model_target_file = model_target_file
|
|
464
|
-
|
|
465
|
-
@property
|
|
466
|
-
def inputs(self) -> Optional[ObjectList]:
|
|
467
|
-
"""input feature list"""
|
|
468
|
-
return self._inputs
|
|
469
|
-
|
|
470
|
-
@inputs.setter
|
|
471
|
-
def inputs(self, inputs: list[Feature]) -> None:
|
|
472
|
-
self._inputs = ObjectList.from_list(Feature, inputs)
|
|
473
|
-
|
|
474
|
-
@property
|
|
475
|
-
def outputs(self) -> Optional[ObjectList]:
|
|
476
|
-
"""output feature list"""
|
|
477
|
-
return self._outputs
|
|
478
|
-
|
|
479
|
-
@outputs.setter
|
|
480
|
-
def outputs(self, outputs: list[Feature]) -> None:
|
|
481
|
-
self._outputs = ObjectList.from_list(Feature, outputs)
|
|
482
|
-
|
|
483
|
-
def infer_from_df(self, df, label_columns=None, with_stats=True, num_bins=None):
|
|
484
|
-
"""infer inputs, outputs, and stats from provided df (training set)
|
|
485
|
-
|
|
486
|
-
:param df: dataframe to infer from
|
|
487
|
-
:param label_columns: name of the label (target) column
|
|
488
|
-
:param with_stats: infer statistics (min, max, .. histogram)
|
|
489
|
-
:param num_bins: number of bins for histogram
|
|
490
|
-
"""
|
|
491
|
-
subset = df
|
|
492
|
-
inferer = get_infer_interface(subset)
|
|
493
|
-
if label_columns:
|
|
494
|
-
if not isinstance(label_columns, list):
|
|
495
|
-
label_columns = [label_columns]
|
|
496
|
-
subset = df.drop(columns=label_columns)
|
|
497
|
-
inferer.infer_schema(subset, self.inputs, {}, options=InferOptions.Features)
|
|
498
|
-
if label_columns:
|
|
499
|
-
inferer.infer_schema(
|
|
500
|
-
df[label_columns], self.outputs, {}, options=InferOptions.Features
|
|
501
|
-
)
|
|
502
|
-
if with_stats:
|
|
503
|
-
self.feature_stats = inferer.get_stats(
|
|
504
|
-
df, options=InferOptions.Histogram, num_bins=num_bins
|
|
505
|
-
)
|
|
506
|
-
|
|
507
|
-
@property
|
|
508
|
-
def is_dir(self):
|
|
509
|
-
return True
|
|
510
|
-
|
|
511
|
-
def before_log(self):
|
|
512
|
-
if not self.model_file:
|
|
513
|
-
raise ValueError("model_file attr must be specified")
|
|
514
|
-
|
|
515
|
-
super().before_log()
|
|
516
|
-
|
|
517
|
-
if self.framework:
|
|
518
|
-
self.labels = self.labels or {}
|
|
519
|
-
self.labels["framework"] = self.framework
|
|
520
|
-
|
|
521
|
-
def upload(self):
|
|
522
|
-
target_model_path = path.join(self.target_path, self.model_file)
|
|
523
|
-
body = self.get_body()
|
|
524
|
-
if body:
|
|
525
|
-
self._upload_body(body, target=target_model_path)
|
|
526
|
-
else:
|
|
527
|
-
src_model_path = _get_src_path(self, self.model_file)
|
|
528
|
-
if not path.isfile(src_model_path):
|
|
529
|
-
raise ValueError(f"model file {src_model_path} not found")
|
|
530
|
-
self._upload_file(src_model_path, target=target_model_path)
|
|
531
|
-
|
|
532
|
-
upload_extra_data(self, self.extra_data)
|
|
533
|
-
|
|
534
|
-
spec_path = path.join(self.target_path, model_spec_filename)
|
|
535
|
-
mlrun.datastore.store_manager.object(url=spec_path).put(self.to_yaml())
|
|
536
|
-
|
|
537
|
-
|
|
538
406
|
def _get_src_path(model_spec: ModelArtifact, filename):
|
|
539
407
|
if model_spec.src_path:
|
|
540
408
|
return path.join(model_spec.src_path, filename)
|
mlrun/artifacts/plots.py
CHANGED
|
@@ -13,14 +13,12 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
import base64
|
|
15
15
|
import typing
|
|
16
|
+
import warnings
|
|
16
17
|
from io import BytesIO
|
|
17
18
|
|
|
18
|
-
from deprecated import deprecated
|
|
19
|
-
|
|
20
19
|
import mlrun
|
|
21
20
|
|
|
22
|
-
from
|
|
23
|
-
from .base import Artifact, LegacyArtifact
|
|
21
|
+
from .base import Artifact
|
|
24
22
|
|
|
25
23
|
if typing.TYPE_CHECKING:
|
|
26
24
|
from plotly.graph_objs import Figure
|
|
@@ -37,6 +35,12 @@ class PlotArtifact(Artifact):
|
|
|
37
35
|
def __init__(
|
|
38
36
|
self, key=None, body=None, is_inline=False, target_path=None, title=None
|
|
39
37
|
):
|
|
38
|
+
if key or body or is_inline or target_path:
|
|
39
|
+
warnings.warn(
|
|
40
|
+
"Artifact constructor parameters are deprecated and will be removed in 1.9.0. "
|
|
41
|
+
"Use the metadata and spec parameters instead.",
|
|
42
|
+
DeprecationWarning,
|
|
43
|
+
)
|
|
40
44
|
super().__init__(key, body, format="html", target_path=target_path)
|
|
41
45
|
self.metadata.description = title
|
|
42
46
|
|
|
@@ -70,138 +74,6 @@ class PlotArtifact(Artifact):
|
|
|
70
74
|
)
|
|
71
75
|
|
|
72
76
|
|
|
73
|
-
# TODO: remove in 1.7.0
|
|
74
|
-
@deprecated(
|
|
75
|
-
version="1.5.0",
|
|
76
|
-
reason="'ChartArtifact' will be removed in 1.7.0, use 'Artifact' instead",
|
|
77
|
-
category=FutureWarning,
|
|
78
|
-
)
|
|
79
|
-
class ChartArtifact(Artifact):
|
|
80
|
-
kind = "chart"
|
|
81
|
-
|
|
82
|
-
_TEMPLATE = """
|
|
83
|
-
<html>
|
|
84
|
-
<head>
|
|
85
|
-
<script
|
|
86
|
-
type="text/javascript"
|
|
87
|
-
src="https://www.gstatic.com/charts/loader.js"></script>
|
|
88
|
-
<script type="text/javascript">
|
|
89
|
-
google.charts.load('current', {'packages':['corechart']});
|
|
90
|
-
google.charts.setOnLoadCallback(drawChart);
|
|
91
|
-
function drawChart() {
|
|
92
|
-
var data = google.visualization.arrayToDataTable($data$);
|
|
93
|
-
var options = $opts$;
|
|
94
|
-
var chart = new google.visualization.$chart$(
|
|
95
|
-
document.getElementById('chart_div'));
|
|
96
|
-
chart.draw(data, options);
|
|
97
|
-
}
|
|
98
|
-
</script>
|
|
99
|
-
</head>
|
|
100
|
-
<body>
|
|
101
|
-
<div id="chart_div" style="width: 100%; height: 500px;"></div>
|
|
102
|
-
</body>
|
|
103
|
-
</html>
|
|
104
|
-
"""
|
|
105
|
-
|
|
106
|
-
def __init__(
|
|
107
|
-
self,
|
|
108
|
-
key=None,
|
|
109
|
-
data=None,
|
|
110
|
-
header=None,
|
|
111
|
-
options=None,
|
|
112
|
-
title=None,
|
|
113
|
-
chart=None,
|
|
114
|
-
target_path=None,
|
|
115
|
-
):
|
|
116
|
-
data = [] if data is None else data
|
|
117
|
-
options = {} if options is None else options
|
|
118
|
-
super().__init__(key, target_path=target_path)
|
|
119
|
-
self.viewer = "chart"
|
|
120
|
-
self.header = header or []
|
|
121
|
-
self.title = title
|
|
122
|
-
self.rows = []
|
|
123
|
-
if data:
|
|
124
|
-
if header:
|
|
125
|
-
self.rows = data
|
|
126
|
-
else:
|
|
127
|
-
self.header = data[0]
|
|
128
|
-
self.rows = data[1:]
|
|
129
|
-
self.options = options
|
|
130
|
-
self.chart = chart or "LineChart"
|
|
131
|
-
self.format = "html"
|
|
132
|
-
|
|
133
|
-
def add_row(self, row):
|
|
134
|
-
self.rows += [row]
|
|
135
|
-
|
|
136
|
-
def get_body(self):
|
|
137
|
-
if not self.options.get("title"):
|
|
138
|
-
self.options["title"] = self.title or self.key
|
|
139
|
-
data = [self.header] + self.rows
|
|
140
|
-
return (
|
|
141
|
-
self._TEMPLATE.replace("$data$", dict_to_json(data))
|
|
142
|
-
.replace("$opts$", dict_to_json(self.options))
|
|
143
|
-
.replace("$chart$", self.chart)
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# TODO: remove in 1.7.0
|
|
148
|
-
@deprecated(
|
|
149
|
-
version="1.5.0",
|
|
150
|
-
reason="'BokehArtifact' will be removed in 1.7.0, use 'Artifact' instead",
|
|
151
|
-
category=FutureWarning,
|
|
152
|
-
)
|
|
153
|
-
class BokehArtifact(Artifact):
|
|
154
|
-
"""
|
|
155
|
-
Bokeh artifact is an artifact for saving Bokeh generated figures. They will be stored in a html format.
|
|
156
|
-
"""
|
|
157
|
-
|
|
158
|
-
kind = "bokeh"
|
|
159
|
-
|
|
160
|
-
def __init__(
|
|
161
|
-
self,
|
|
162
|
-
figure=None,
|
|
163
|
-
key: str = None,
|
|
164
|
-
target_path: str = None,
|
|
165
|
-
):
|
|
166
|
-
"""
|
|
167
|
-
Initialize a Bokeh artifact with the given figure.
|
|
168
|
-
|
|
169
|
-
:param figure: Bokeh figure ('bokeh.plotting.Figure' object) to save as an artifact.
|
|
170
|
-
:param key: Key for the artifact to be stored in the database.
|
|
171
|
-
:param target_path: Path to save the artifact.
|
|
172
|
-
"""
|
|
173
|
-
# Validate input:
|
|
174
|
-
try:
|
|
175
|
-
from bokeh.plotting import Figure
|
|
176
|
-
except (ModuleNotFoundError, ImportError) as Error:
|
|
177
|
-
raise Error(
|
|
178
|
-
"Using 'BokehArtifact' requires bokeh package. Use pip install mlrun[bokeh] to install it."
|
|
179
|
-
)
|
|
180
|
-
if figure is not None and not isinstance(figure, Figure):
|
|
181
|
-
raise ValueError(
|
|
182
|
-
"BokehArtifact requires the figure parameter to be a "
|
|
183
|
-
f"'bokeh.plotting.Figure' but received '{type(figure)}'"
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
# Call the artifact initializer:
|
|
187
|
-
super().__init__(key=key, target_path=target_path, viewer="bokeh")
|
|
188
|
-
|
|
189
|
-
# Continue initializing the bokeh artifact:
|
|
190
|
-
self._figure = figure
|
|
191
|
-
self.spec.format = "html"
|
|
192
|
-
|
|
193
|
-
def get_body(self):
|
|
194
|
-
"""
|
|
195
|
-
Get the artifact's body - the bokeh figure's html code.
|
|
196
|
-
|
|
197
|
-
:return: The figure's html code.
|
|
198
|
-
"""
|
|
199
|
-
from bokeh.embed import file_html
|
|
200
|
-
from bokeh.resources import CDN
|
|
201
|
-
|
|
202
|
-
return file_html(self._figure, CDN, self.metadata.key)
|
|
203
|
-
|
|
204
|
-
|
|
205
77
|
class PlotlyArtifact(Artifact):
|
|
206
78
|
"""
|
|
207
79
|
Plotly artifact is an artifact for saving Plotly generated figures. They will be stored in a html format.
|
|
@@ -222,6 +94,12 @@ class PlotlyArtifact(Artifact):
|
|
|
222
94
|
:param key: Key for the artifact to be stored in the database.
|
|
223
95
|
:param target_path: Path to save the artifact.
|
|
224
96
|
"""
|
|
97
|
+
if key or target_path:
|
|
98
|
+
warnings.warn(
|
|
99
|
+
"Artifact constructor parameters are deprecated and will be removed in 1.9.0. "
|
|
100
|
+
"Use the metadata and spec parameters instead.",
|
|
101
|
+
DeprecationWarning,
|
|
102
|
+
)
|
|
225
103
|
# Validate the plotly package:
|
|
226
104
|
try:
|
|
227
105
|
from plotly.graph_objs import Figure
|
|
@@ -258,242 +136,3 @@ class PlotlyArtifact(Artifact):
|
|
|
258
136
|
:return: The figure's html code.
|
|
259
137
|
"""
|
|
260
138
|
return self._figure.to_html()
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
# TODO: remove in 1.7.0
|
|
264
|
-
@deprecated(
|
|
265
|
-
version="1.3.0",
|
|
266
|
-
reason="'LegacyPlotArtifact' will be removed in 1.7.0, use 'PlotArtifact' instead",
|
|
267
|
-
category=FutureWarning,
|
|
268
|
-
)
|
|
269
|
-
class LegacyPlotArtifact(LegacyArtifact):
|
|
270
|
-
_TEMPLATE = """
|
|
271
|
-
<h3 style="text-align:center">{}</h3>
|
|
272
|
-
<img title="{}" src="data:image/png;base64,{}">
|
|
273
|
-
"""
|
|
274
|
-
kind = "plot"
|
|
275
|
-
|
|
276
|
-
def __init__(
|
|
277
|
-
self, key=None, body=None, is_inline=False, target_path=None, title=None
|
|
278
|
-
):
|
|
279
|
-
super().__init__(key, body, format="html", target_path=target_path)
|
|
280
|
-
self.description = title
|
|
281
|
-
|
|
282
|
-
def before_log(self):
|
|
283
|
-
self.viewer = "chart"
|
|
284
|
-
import matplotlib
|
|
285
|
-
|
|
286
|
-
if not self._body or not isinstance(
|
|
287
|
-
self._body, (bytes, matplotlib.figure.Figure)
|
|
288
|
-
):
|
|
289
|
-
raise ValueError(
|
|
290
|
-
"matplotlib fig or png bytes must be provided as artifact body"
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
def get_body(self):
|
|
294
|
-
"""Convert Matplotlib figure 'fig' into a <img> tag for HTML use
|
|
295
|
-
using base64 encoding."""
|
|
296
|
-
if isinstance(self._body, bytes):
|
|
297
|
-
data = self._body
|
|
298
|
-
else:
|
|
299
|
-
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
|
|
300
|
-
|
|
301
|
-
canvas = FigureCanvas(self._body)
|
|
302
|
-
png_output = BytesIO()
|
|
303
|
-
canvas.print_png(png_output)
|
|
304
|
-
data = png_output.getvalue()
|
|
305
|
-
|
|
306
|
-
data_uri = base64.b64encode(data).decode("utf-8")
|
|
307
|
-
return self._TEMPLATE.format(self.description or self.key, self.key, data_uri)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
# TODO: remove in 1.7.0
|
|
311
|
-
@deprecated(
|
|
312
|
-
version="1.3.0",
|
|
313
|
-
reason="'LegacyChartArtifact' will be removed in 1.7.0, use 'ChartArtifact' instead",
|
|
314
|
-
category=FutureWarning,
|
|
315
|
-
)
|
|
316
|
-
class LegacyChartArtifact(LegacyArtifact):
|
|
317
|
-
_TEMPLATE = """
|
|
318
|
-
<html>
|
|
319
|
-
<head>
|
|
320
|
-
<script
|
|
321
|
-
type="text/javascript"
|
|
322
|
-
src="https://www.gstatic.com/charts/loader.js"></script>
|
|
323
|
-
<script type="text/javascript">
|
|
324
|
-
google.charts.load('current', {'packages':['corechart']});
|
|
325
|
-
google.charts.setOnLoadCallback(drawChart);
|
|
326
|
-
function drawChart() {
|
|
327
|
-
var data = google.visualization.arrayToDataTable($data$);
|
|
328
|
-
var options = $opts$;
|
|
329
|
-
var chart = new google.visualization.$chart$(
|
|
330
|
-
document.getElementById('chart_div'));
|
|
331
|
-
chart.draw(data, options);
|
|
332
|
-
}
|
|
333
|
-
</script>
|
|
334
|
-
</head>
|
|
335
|
-
<body>
|
|
336
|
-
<div id="chart_div" style="width: 100%; height: 500px;"></div>
|
|
337
|
-
</body>
|
|
338
|
-
</html>
|
|
339
|
-
"""
|
|
340
|
-
|
|
341
|
-
kind = "chart"
|
|
342
|
-
|
|
343
|
-
def __init__(
|
|
344
|
-
self,
|
|
345
|
-
key=None,
|
|
346
|
-
data=None,
|
|
347
|
-
header=None,
|
|
348
|
-
options=None,
|
|
349
|
-
title=None,
|
|
350
|
-
chart=None,
|
|
351
|
-
target_path=None,
|
|
352
|
-
):
|
|
353
|
-
data = [] if data is None else data
|
|
354
|
-
options = {} if options is None else options
|
|
355
|
-
super().__init__(key, target_path=target_path)
|
|
356
|
-
self.viewer = "chart"
|
|
357
|
-
self.header = header or []
|
|
358
|
-
self.title = title
|
|
359
|
-
self.rows = []
|
|
360
|
-
if data:
|
|
361
|
-
if header:
|
|
362
|
-
self.rows = data
|
|
363
|
-
else:
|
|
364
|
-
self.header = data[0]
|
|
365
|
-
self.rows = data[1:]
|
|
366
|
-
self.options = options
|
|
367
|
-
self.chart = chart or "LineChart"
|
|
368
|
-
self.format = "html"
|
|
369
|
-
|
|
370
|
-
def add_row(self, row):
|
|
371
|
-
self.rows += [row]
|
|
372
|
-
|
|
373
|
-
def get_body(self):
|
|
374
|
-
if not self.options.get("title"):
|
|
375
|
-
self.options["title"] = self.title or self.key
|
|
376
|
-
data = [self.header] + self.rows
|
|
377
|
-
return (
|
|
378
|
-
self._TEMPLATE.replace("$data$", dict_to_json(data))
|
|
379
|
-
.replace("$opts$", dict_to_json(self.options))
|
|
380
|
-
.replace("$chart$", self.chart)
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
# TODO: remove in 1.7.0
|
|
385
|
-
@deprecated(
|
|
386
|
-
version="1.3.0",
|
|
387
|
-
reason="'LegacyBokehArtifact' will be removed in 1.7.0, use 'BokehArtifact' instead",
|
|
388
|
-
category=FutureWarning,
|
|
389
|
-
)
|
|
390
|
-
class LegacyBokehArtifact(LegacyArtifact):
|
|
391
|
-
"""
|
|
392
|
-
Bokeh artifact is an artifact for saving Bokeh generated figures. They will be stored in a html format.
|
|
393
|
-
"""
|
|
394
|
-
|
|
395
|
-
kind = "bokeh"
|
|
396
|
-
|
|
397
|
-
def __init__(
|
|
398
|
-
self,
|
|
399
|
-
figure=None,
|
|
400
|
-
key: str = None,
|
|
401
|
-
target_path: str = None,
|
|
402
|
-
):
|
|
403
|
-
"""
|
|
404
|
-
Initialize a Bokeh artifact with the given figure.
|
|
405
|
-
:param figure: Bokeh figure ('bokeh.plotting.Figure' object) to save as an artifact.
|
|
406
|
-
:param key: Key for the artifact to be stored in the database.
|
|
407
|
-
:param target_path: Path to save the artifact.
|
|
408
|
-
"""
|
|
409
|
-
# Validate input:
|
|
410
|
-
try:
|
|
411
|
-
from bokeh.plotting import Figure
|
|
412
|
-
except (ModuleNotFoundError, ImportError) as Error:
|
|
413
|
-
raise Error(
|
|
414
|
-
"Using 'BokehArtifact' requires bokeh package. Use pip install mlrun[bokeh] to install it."
|
|
415
|
-
)
|
|
416
|
-
if figure is not None and not isinstance(figure, Figure):
|
|
417
|
-
raise ValueError(
|
|
418
|
-
"BokehArtifact requires the figure parameter to be a "
|
|
419
|
-
f"'bokeh.plotting.Figure' but received '{type(figure)}'"
|
|
420
|
-
)
|
|
421
|
-
|
|
422
|
-
# Call the artifact initializer:
|
|
423
|
-
super().__init__(key=key, target_path=target_path, viewer="bokeh")
|
|
424
|
-
|
|
425
|
-
# Continue initializing the bokeh artifact:
|
|
426
|
-
self._figure = figure
|
|
427
|
-
self.format = "html"
|
|
428
|
-
|
|
429
|
-
def get_body(self):
|
|
430
|
-
"""
|
|
431
|
-
Get the artifact's body - the bokeh figure's html code.
|
|
432
|
-
:return: The figure's html code.
|
|
433
|
-
"""
|
|
434
|
-
from bokeh.embed import file_html
|
|
435
|
-
from bokeh.resources import CDN
|
|
436
|
-
|
|
437
|
-
return file_html(self._figure, CDN, self.key)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
# TODO: remove in 1.7.0
|
|
441
|
-
@deprecated(
|
|
442
|
-
version="1.3.0",
|
|
443
|
-
reason="'LegacyPlotlyArtifact' will be removed in 1.7.0, use 'PlotlyArtifact' instead",
|
|
444
|
-
category=FutureWarning,
|
|
445
|
-
)
|
|
446
|
-
class LegacyPlotlyArtifact(LegacyArtifact):
|
|
447
|
-
"""
|
|
448
|
-
Plotly artifact is an artifact for saving Plotly generated figures. They will be stored in a html format.
|
|
449
|
-
"""
|
|
450
|
-
|
|
451
|
-
kind = "plotly"
|
|
452
|
-
|
|
453
|
-
def __init__(
|
|
454
|
-
self,
|
|
455
|
-
figure=None,
|
|
456
|
-
key: str = None,
|
|
457
|
-
target_path: str = None,
|
|
458
|
-
):
|
|
459
|
-
"""
|
|
460
|
-
Initialize a Plotly artifact with the given figure.
|
|
461
|
-
:param figure: Plotly figure ('plotly.graph_objs.Figure' object) to save as an artifact.
|
|
462
|
-
:param key: Key for the artifact to be stored in the database.
|
|
463
|
-
:param target_path: Path to save the artifact.
|
|
464
|
-
"""
|
|
465
|
-
# Validate the plotly package:
|
|
466
|
-
try:
|
|
467
|
-
from plotly.graph_objs import Figure
|
|
468
|
-
except ModuleNotFoundError:
|
|
469
|
-
raise mlrun.errors.MLRunMissingDependencyError(
|
|
470
|
-
"Using `PlotlyArtifact` requires plotly package. Use `pip install mlrun[plotly]` to install it."
|
|
471
|
-
)
|
|
472
|
-
except ImportError:
|
|
473
|
-
import plotly
|
|
474
|
-
|
|
475
|
-
raise mlrun.errors.MLRunMissingDependencyError(
|
|
476
|
-
f"Using `PlotlyArtifact` requires plotly version >= 5.4.0 but found version {plotly.__version__}. "
|
|
477
|
-
f"Use `pip install -U mlrun[plotly]` to install it."
|
|
478
|
-
)
|
|
479
|
-
|
|
480
|
-
# Call the artifact initializer:
|
|
481
|
-
super().__init__(key=key, target_path=target_path, viewer="plotly")
|
|
482
|
-
|
|
483
|
-
# Validate input:
|
|
484
|
-
if figure is not None and not isinstance(figure, Figure):
|
|
485
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
486
|
-
f"PlotlyArtifact requires the figure parameter to be a "
|
|
487
|
-
f"`plotly.graph_objs.Figure` but received '{type(figure)}'"
|
|
488
|
-
)
|
|
489
|
-
|
|
490
|
-
# Continue initializing the plotly artifact:
|
|
491
|
-
self._figure = figure
|
|
492
|
-
self.format = "html"
|
|
493
|
-
|
|
494
|
-
def get_body(self):
|
|
495
|
-
"""
|
|
496
|
-
Get the artifact's body - the Plotly figure's html code.
|
|
497
|
-
:return: The figure's html code.
|
|
498
|
-
"""
|
|
499
|
-
return self._figure.to_html()
|
mlrun/common/schemas/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
# flake8: noqa - this is until we take care of the F401 violations with respect to __all__ & sphinx
|
|
16
16
|
|
|
17
|
-
from .alert import AlertActiveState, AlertConfig, Event
|
|
17
|
+
from .alert import AlertActiveState, AlertConfig, AlertTemplate, Event
|
|
18
18
|
from .api_gateway import (
|
|
19
19
|
APIGateway,
|
|
20
20
|
APIGatewayAuthenticationMode,
|
|
@@ -142,8 +142,10 @@ from .model_monitoring import (
|
|
|
142
142
|
ModelMonitoringMode,
|
|
143
143
|
ModelMonitoringStoreKinds,
|
|
144
144
|
MonitoringFunctionNames,
|
|
145
|
+
MonitoringTSDBTables,
|
|
145
146
|
PrometheusEndpoints,
|
|
146
|
-
|
|
147
|
+
TimeSeriesConnector,
|
|
148
|
+
TSDBTarget,
|
|
147
149
|
)
|
|
148
150
|
from .notification import (
|
|
149
151
|
Notification,
|