mlrun 1.7.0rc13__py3-none-any.whl → 1.7.0rc21__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 +23 -111
- mlrun/alerts/__init__.py +15 -0
- mlrun/alerts/alert.py +144 -0
- mlrun/api/schemas/__init__.py +4 -3
- mlrun/artifacts/__init__.py +8 -3
- mlrun/artifacts/base.py +36 -253
- mlrun/artifacts/dataset.py +9 -190
- mlrun/artifacts/manager.py +46 -42
- mlrun/artifacts/model.py +9 -141
- mlrun/artifacts/plots.py +14 -375
- mlrun/common/constants.py +65 -3
- mlrun/common/formatters/__init__.py +19 -0
- mlrun/{runtimes/mpijob/v1alpha1.py → common/formatters/artifact.py} +6 -14
- mlrun/common/formatters/base.py +113 -0
- mlrun/common/formatters/function.py +46 -0
- mlrun/common/formatters/pipeline.py +53 -0
- mlrun/common/formatters/project.py +51 -0
- mlrun/{runtimes → common/runtimes}/constants.py +32 -4
- mlrun/common/schemas/__init__.py +10 -5
- mlrun/common/schemas/alert.py +92 -11
- mlrun/common/schemas/api_gateway.py +56 -0
- mlrun/common/schemas/artifact.py +15 -5
- mlrun/common/schemas/auth.py +2 -0
- mlrun/common/schemas/client_spec.py +1 -0
- mlrun/common/schemas/frontend_spec.py +1 -0
- mlrun/common/schemas/function.py +4 -0
- mlrun/common/schemas/model_monitoring/__init__.py +15 -3
- mlrun/common/schemas/model_monitoring/constants.py +58 -7
- mlrun/common/schemas/model_monitoring/grafana.py +9 -5
- mlrun/common/schemas/model_monitoring/model_endpoints.py +86 -2
- mlrun/common/schemas/pipeline.py +0 -9
- mlrun/common/schemas/project.py +6 -11
- mlrun/common/types.py +1 -0
- mlrun/config.py +36 -8
- mlrun/data_types/to_pandas.py +9 -9
- mlrun/datastore/base.py +41 -9
- mlrun/datastore/datastore.py +6 -2
- mlrun/datastore/datastore_profile.py +56 -4
- mlrun/datastore/hdfs.py +5 -0
- mlrun/datastore/inmem.py +2 -2
- mlrun/datastore/redis.py +2 -2
- mlrun/datastore/s3.py +5 -0
- mlrun/datastore/sources.py +147 -7
- mlrun/datastore/store_resources.py +7 -7
- mlrun/datastore/targets.py +129 -9
- mlrun/datastore/utils.py +42 -0
- mlrun/datastore/v3io.py +1 -1
- mlrun/db/auth_utils.py +152 -0
- mlrun/db/base.py +55 -11
- mlrun/db/httpdb.py +346 -107
- mlrun/db/nopdb.py +52 -10
- mlrun/errors.py +11 -0
- mlrun/execution.py +24 -9
- 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/feature_vector.py +8 -0
- mlrun/feature_store/ingestion.py +7 -6
- mlrun/feature_store/retrieval/base.py +9 -4
- mlrun/feature_store/retrieval/conversion.py +9 -9
- mlrun/feature_store/retrieval/dask_merger.py +2 -0
- mlrun/feature_store/retrieval/job.py +9 -3
- mlrun/feature_store/retrieval/local_merger.py +2 -0
- mlrun/feature_store/retrieval/spark_merger.py +16 -0
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
- mlrun/frameworks/parallel_coordinates.py +2 -1
- mlrun/frameworks/tf_keras/__init__.py +4 -1
- mlrun/k8s_utils.py +10 -11
- mlrun/launcher/base.py +4 -3
- mlrun/launcher/client.py +5 -3
- mlrun/launcher/local.py +8 -2
- mlrun/launcher/remote.py +8 -2
- mlrun/lists.py +6 -2
- mlrun/model.py +62 -20
- 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 +280 -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/__init__.py +0 -2
- mlrun/model_monitoring/db/stores/base/store.py +22 -37
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +43 -21
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +39 -8
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +27 -7
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +5 -0
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +246 -224
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +232 -216
- mlrun/model_monitoring/db/tsdb/__init__.py +100 -0
- mlrun/model_monitoring/db/tsdb/base.py +329 -0
- mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
- mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +240 -0
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +45 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +397 -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 +636 -0
- mlrun/model_monitoring/evidently_application.py +6 -118
- mlrun/model_monitoring/helpers.py +46 -1
- mlrun/model_monitoring/model_endpoint.py +3 -2
- mlrun/model_monitoring/stream_processing.py +57 -216
- mlrun/model_monitoring/writer.py +134 -124
- mlrun/package/utils/_formatter.py +2 -2
- mlrun/platforms/__init__.py +10 -9
- mlrun/platforms/iguazio.py +21 -202
- mlrun/projects/operations.py +19 -12
- mlrun/projects/pipelines.py +103 -109
- mlrun/projects/project.py +377 -137
- mlrun/render.py +15 -14
- mlrun/run.py +16 -47
- mlrun/runtimes/__init__.py +6 -3
- mlrun/runtimes/base.py +8 -7
- mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
- mlrun/runtimes/funcdoc.py +0 -28
- mlrun/runtimes/kubejob.py +2 -1
- mlrun/runtimes/local.py +5 -2
- mlrun/runtimes/mpijob/__init__.py +0 -20
- mlrun/runtimes/mpijob/v1.py +1 -1
- mlrun/runtimes/nuclio/api_gateway.py +440 -208
- mlrun/runtimes/nuclio/application/application.py +170 -8
- mlrun/runtimes/nuclio/function.py +39 -49
- mlrun/runtimes/pod.py +21 -41
- mlrun/runtimes/remotesparkjob.py +9 -3
- mlrun/runtimes/sparkjob/spark3job.py +1 -1
- mlrun/runtimes/utils.py +6 -45
- mlrun/serving/server.py +2 -1
- mlrun/serving/states.py +53 -2
- mlrun/serving/v2_serving.py +5 -1
- mlrun/track/tracker.py +2 -1
- mlrun/utils/async_http.py +25 -5
- mlrun/utils/helpers.py +107 -75
- mlrun/utils/logger.py +39 -7
- mlrun/utils/notifications/notification/__init__.py +14 -9
- mlrun/utils/notifications/notification/base.py +1 -1
- mlrun/utils/notifications/notification/slack.py +61 -13
- mlrun/utils/notifications/notification/webhook.py +1 -1
- mlrun/utils/notifications/notification_pusher.py +147 -16
- mlrun/utils/regex.py +9 -0
- mlrun/utils/v3io_clients.py +0 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/METADATA +14 -6
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/RECORD +154 -133
- mlrun/kfpops.py +0 -865
- mlrun/platforms/other.py +0 -305
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/top_level.txt +0 -0
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/constants.py
CHANGED
|
@@ -12,12 +12,74 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
#
|
|
15
|
+
|
|
15
16
|
IMAGE_NAME_ENRICH_REGISTRY_PREFIX = "." # prefix for image name to enrich with registry
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
MLRUN_SERVING_SPEC_MOUNT_PATH = f"/tmp/mlrun/{MLRUN_MODEL_CONF}"
|
|
17
|
+
MLRUN_SERVING_CONF = "serving-conf"
|
|
18
|
+
MLRUN_SERVING_SPEC_MOUNT_PATH = f"/tmp/mlrun/{MLRUN_SERVING_CONF}"
|
|
19
19
|
MLRUN_SERVING_SPEC_FILENAME = "serving_spec.json"
|
|
20
20
|
MLRUN_SERVING_SPEC_PATH = (
|
|
21
21
|
f"{MLRUN_SERVING_SPEC_MOUNT_PATH}/{MLRUN_SERVING_SPEC_FILENAME}"
|
|
22
22
|
)
|
|
23
|
+
MLRUN_FUNCTIONS_ANNOTATION = "mlrun/mlrun-functions"
|
|
23
24
|
MYSQL_MEDIUMBLOB_SIZE_BYTES = 16 * 1024 * 1024
|
|
25
|
+
MLRUN_LABEL_PREFIX = "mlrun/"
|
|
26
|
+
DASK_LABEL_PREFIX = "dask.org/"
|
|
27
|
+
NUCLIO_LABEL_PREFIX = "nuclio.io/"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MLRunInternalLabels:
|
|
31
|
+
### dask
|
|
32
|
+
dask_cluster_name = f"{DASK_LABEL_PREFIX}cluster-name"
|
|
33
|
+
dask_component = f"{DASK_LABEL_PREFIX}component"
|
|
34
|
+
|
|
35
|
+
### spark
|
|
36
|
+
spark_role = "spark-role"
|
|
37
|
+
|
|
38
|
+
### mpi
|
|
39
|
+
mpi_job_name = "mpi-job-name"
|
|
40
|
+
mpi_job_role = "mpi-job-role"
|
|
41
|
+
mpi_role_type = "mpi_role_type"
|
|
42
|
+
|
|
43
|
+
### nuclio
|
|
44
|
+
nuclio_project_name = f"{NUCLIO_LABEL_PREFIX}project-name"
|
|
45
|
+
nuclio_function_name = f"{NUCLIO_LABEL_PREFIX}function-name"
|
|
46
|
+
nuclio_class = f"{NUCLIO_LABEL_PREFIX}class"
|
|
47
|
+
|
|
48
|
+
### mlrun
|
|
49
|
+
mlrun_auth_key = "mlrun-auth-key"
|
|
50
|
+
mlrun_class = f"{MLRUN_LABEL_PREFIX}class"
|
|
51
|
+
client_python_version = f"{MLRUN_LABEL_PREFIX}client_python_version"
|
|
52
|
+
client_version = f"{MLRUN_LABEL_PREFIX}client_version"
|
|
53
|
+
function = f"{MLRUN_LABEL_PREFIX}function"
|
|
54
|
+
job = f"{MLRUN_LABEL_PREFIX}job"
|
|
55
|
+
name = f"{MLRUN_LABEL_PREFIX}name"
|
|
56
|
+
mlrun_owner = f"{MLRUN_LABEL_PREFIX}owner"
|
|
57
|
+
owner_domain = f"{MLRUN_LABEL_PREFIX}owner_domain"
|
|
58
|
+
project = f"{MLRUN_LABEL_PREFIX}project"
|
|
59
|
+
runner_pod = f"{MLRUN_LABEL_PREFIX}runner-pod"
|
|
60
|
+
schedule_name = f"{MLRUN_LABEL_PREFIX}schedule-name"
|
|
61
|
+
scrape_metrics = f"{MLRUN_LABEL_PREFIX}scrape-metrics"
|
|
62
|
+
tag = f"{MLRUN_LABEL_PREFIX}tag"
|
|
63
|
+
uid = f"{MLRUN_LABEL_PREFIX}uid"
|
|
64
|
+
username = f"{MLRUN_LABEL_PREFIX}username"
|
|
65
|
+
username_domain = f"{MLRUN_LABEL_PREFIX}username_domain"
|
|
66
|
+
task_name = f"{MLRUN_LABEL_PREFIX}task-name"
|
|
67
|
+
host = "host"
|
|
68
|
+
job_type = "job-type"
|
|
69
|
+
kind = "kind"
|
|
70
|
+
component = "component"
|
|
71
|
+
resource_name = "resource_name"
|
|
72
|
+
created = "mlrun-created"
|
|
73
|
+
|
|
74
|
+
owner = "owner"
|
|
75
|
+
v3io_user = "v3io_user"
|
|
76
|
+
workflow = "workflow"
|
|
77
|
+
feature_vector = "feature-vector"
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def all(cls):
|
|
81
|
+
return [
|
|
82
|
+
value
|
|
83
|
+
for key, value in cls.__dict__.items()
|
|
84
|
+
if not key.startswith("__") and isinstance(value, str)
|
|
85
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright 2024 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
|
+
#
|
|
15
|
+
|
|
16
|
+
from .artifact import ArtifactFormat # noqa
|
|
17
|
+
from .function import FunctionFormat # noqa
|
|
18
|
+
from .pipeline import PipelineFormat # noqa
|
|
19
|
+
from .project import ProjectFormat # noqa
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2024 Iguazio
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -11,19 +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
|
+
#
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
from mlrun.runtimes.mpijob.abstract import AbstractMPIJobRuntime
|
|
16
|
+
import mlrun.common.types
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
# TODO:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
reason="v1alpha1 mpi will be removed in 1.7.0, use v1 instead",
|
|
24
|
-
category=FutureWarning,
|
|
25
|
-
)
|
|
26
|
-
class MpiRuntimeV1Alpha1(AbstractMPIJobRuntime):
|
|
27
|
-
crd_group = "kubeflow.org"
|
|
28
|
-
crd_version = MPIJobCRDVersions.v1alpha1
|
|
29
|
-
crd_plural = "mpijobs"
|
|
19
|
+
# TODO: add a format that returns a minimal response with ObjectFormat
|
|
20
|
+
class ArtifactFormat(mlrun.common.types.StrEnum):
|
|
21
|
+
full = "full"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Copyright 2024 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
|
+
#
|
|
15
|
+
|
|
16
|
+
import typing
|
|
17
|
+
|
|
18
|
+
import mlrun.errors
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ObjectFormat:
|
|
22
|
+
"""
|
|
23
|
+
MLRun object formatter. Any class that inherits from this class should implement the `format_method` method
|
|
24
|
+
to specify the formatting method for each format.
|
|
25
|
+
A `filter_obj_method` utility method is provided to filter the object based on a list of keys.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
full = "full"
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def format_method(_format: str) -> typing.Optional[typing.Callable]:
|
|
32
|
+
"""
|
|
33
|
+
Get the formatting method for the provided format.
|
|
34
|
+
A `None` value signifies a pass-through formatting method (no formatting).
|
|
35
|
+
:param _format: The format as a string representation.
|
|
36
|
+
:return: The formatting method.
|
|
37
|
+
"""
|
|
38
|
+
return {
|
|
39
|
+
ObjectFormat.full: None,
|
|
40
|
+
}[_format]
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def format_obj(
|
|
44
|
+
cls,
|
|
45
|
+
obj: typing.Any,
|
|
46
|
+
_format: str,
|
|
47
|
+
exclude_formats: typing.Optional[list[str]] = None,
|
|
48
|
+
) -> typing.Any:
|
|
49
|
+
"""
|
|
50
|
+
Format the provided object based on the provided format.
|
|
51
|
+
:param obj: The object to format.
|
|
52
|
+
:param _format: The format as a string representation.
|
|
53
|
+
:param exclude_formats: A list of formats to exclude from the formatting process. If the provided format is in
|
|
54
|
+
this list, an invalid format exception will be raised.
|
|
55
|
+
"""
|
|
56
|
+
exclude_formats = exclude_formats or []
|
|
57
|
+
_format = _format or cls.full
|
|
58
|
+
invalid_format_exc = mlrun.errors.MLRunBadRequestError(
|
|
59
|
+
f"Provided format is not supported. format={_format}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if _format in exclude_formats:
|
|
63
|
+
raise invalid_format_exc
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
format_method = cls.format_method(_format)
|
|
67
|
+
except KeyError:
|
|
68
|
+
raise invalid_format_exc
|
|
69
|
+
|
|
70
|
+
if not format_method:
|
|
71
|
+
return obj
|
|
72
|
+
|
|
73
|
+
return format_method(obj)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def filter_obj_method(_filter: list[str]) -> typing.Callable:
|
|
77
|
+
"""
|
|
78
|
+
Returns a method that filters the object based on the provided list of keys.
|
|
79
|
+
The keys should be in a dot-separated format, denoting the path within the dictionary to the desired key.
|
|
80
|
+
The object maintains its structure, with the filtered keys and their values, while all other keys are removed.
|
|
81
|
+
:param _filter: The list of keys to filter by.
|
|
82
|
+
Example:
|
|
83
|
+
[
|
|
84
|
+
"kind",
|
|
85
|
+
"metadata.name",
|
|
86
|
+
"spec.something.else",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
:return: The filtering method.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def _filter_method(obj: dict) -> dict:
|
|
93
|
+
formatted_obj = {}
|
|
94
|
+
for key in _filter:
|
|
95
|
+
key_list = key.split(".")
|
|
96
|
+
obj_recursive_iterator = obj
|
|
97
|
+
formatted_obj_recursive_iterator = formatted_obj
|
|
98
|
+
for idx, key in enumerate(key_list):
|
|
99
|
+
if key not in obj_recursive_iterator:
|
|
100
|
+
break
|
|
101
|
+
value = (
|
|
102
|
+
{} if idx < len(key_list) - 1 else obj_recursive_iterator[key]
|
|
103
|
+
)
|
|
104
|
+
formatted_obj_recursive_iterator.setdefault(key, value)
|
|
105
|
+
|
|
106
|
+
obj_recursive_iterator = obj_recursive_iterator[key]
|
|
107
|
+
formatted_obj_recursive_iterator = formatted_obj_recursive_iterator[
|
|
108
|
+
key
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
return formatted_obj
|
|
112
|
+
|
|
113
|
+
return _filter_method
|