mlrun 1.9.0rc3__py3-none-any.whl → 1.9.0rc5__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/__main__.py +13 -3
- mlrun/artifacts/base.py +5 -5
- mlrun/artifacts/dataset.py +1 -1
- mlrun/artifacts/model.py +1 -1
- mlrun/artifacts/plots.py +2 -2
- mlrun/common/constants.py +7 -0
- mlrun/common/runtimes/constants.py +1 -1
- mlrun/common/schemas/artifact.py +1 -1
- mlrun/common/schemas/model_monitoring/model_endpoints.py +32 -8
- mlrun/common/schemas/pipeline.py +1 -1
- mlrun/common/schemas/project.py +1 -1
- mlrun/common/schemas/runs.py +1 -1
- mlrun/config.py +5 -11
- mlrun/datastore/datastore.py +1 -1
- mlrun/datastore/datastore_profile.py +2 -2
- mlrun/datastore/sources.py +3 -3
- mlrun/datastore/targets.py +4 -4
- mlrun/datastore/utils.py +2 -2
- mlrun/db/base.py +9 -7
- mlrun/db/httpdb.py +48 -27
- mlrun/db/nopdb.py +3 -1
- mlrun/execution.py +1 -1
- mlrun/frameworks/_common/model_handler.py +2 -2
- mlrun/launcher/client.py +1 -1
- mlrun/model_monitoring/api.py +4 -4
- mlrun/model_monitoring/applications/_application_steps.py +3 -1
- mlrun/model_monitoring/applications/evidently/base.py +59 -71
- mlrun/model_monitoring/controller.py +26 -11
- mlrun/model_monitoring/db/tsdb/base.py +3 -1
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +213 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +27 -49
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +48 -35
- mlrun/model_monitoring/tracking_policy.py +1 -1
- mlrun/model_monitoring/writer.py +1 -1
- mlrun/projects/operations.py +3 -3
- mlrun/projects/project.py +37 -22
- mlrun/render.py +5 -9
- mlrun/run.py +1 -1
- mlrun/runtimes/base.py +5 -5
- mlrun/runtimes/kubejob.py +2 -2
- mlrun/runtimes/nuclio/function.py +3 -3
- mlrun/runtimes/nuclio/serving.py +4 -4
- mlrun/runtimes/utils.py +25 -8
- mlrun/utils/helpers.py +3 -2
- mlrun/utils/notifications/notification/webhook.py +18 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/METADATA +9 -13
- {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/RECORD +52 -51
- {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/WHEEL +1 -1
- {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/entry_points.txt +0 -0
- {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/top_level.txt +0 -0
mlrun/db/httpdb.py
CHANGED
|
@@ -21,7 +21,7 @@ import typing
|
|
|
21
21
|
import warnings
|
|
22
22
|
from copy import deepcopy
|
|
23
23
|
from datetime import datetime, timedelta
|
|
24
|
-
from os import path, remove
|
|
24
|
+
from os import environ, path, remove
|
|
25
25
|
from typing import Literal, Optional, Union
|
|
26
26
|
from urllib.parse import urlparse
|
|
27
27
|
|
|
@@ -129,7 +129,9 @@ class HTTPRunDB(RunDBInterface):
|
|
|
129
129
|
self._wait_for_background_task_terminal_state_retry_interval = 3
|
|
130
130
|
self._wait_for_project_deletion_interval = 3
|
|
131
131
|
self.client_version = version.Version().get()["version"]
|
|
132
|
-
self.python_version =
|
|
132
|
+
self.python_version = environ.get("MLRUN_PYTHON_VERSION") or str(
|
|
133
|
+
version.Version().get_python_version()
|
|
134
|
+
)
|
|
133
135
|
|
|
134
136
|
self._enrich_and_validate(url)
|
|
135
137
|
|
|
@@ -945,7 +947,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
945
947
|
or just `"label"` for key existence.
|
|
946
948
|
- A comma-separated string formatted as `"label1=value1,label2"` to match entities with
|
|
947
949
|
the specified key-value pairs or key existence.
|
|
948
|
-
:param state: Deprecated - List only runs whose state is specified (will be removed in 1.
|
|
950
|
+
:param state: Deprecated - List only runs whose state is specified (will be removed in 1.10.0)
|
|
949
951
|
:param states: List only runs whose state is one of the provided states.
|
|
950
952
|
:param sort: Whether to sort the result according to their start time. Otherwise, results will be
|
|
951
953
|
returned by their internal order in the DB (order will not be guaranteed).
|
|
@@ -1276,8 +1278,8 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1276
1278
|
:param producer_uri: Return artifacts produced by the requested producer URI. Producer URI usually
|
|
1277
1279
|
points to a run and is used to filter artifacts by the run that produced them when the artifact producer id
|
|
1278
1280
|
is a workflow id (artifact was created as part of a workflow).
|
|
1279
|
-
:param format_:
|
|
1280
|
-
:param limit:
|
|
1281
|
+
:param format_: The format in which to return the artifacts. Default is 'full'.
|
|
1282
|
+
:param limit: Deprecated - Maximum number of artifacts to return (will be removed in 1.11.0).
|
|
1281
1283
|
:param partition_by: Field to group results by. When `partition_by` is specified, the `partition_sort_by`
|
|
1282
1284
|
parameter must be provided as well.
|
|
1283
1285
|
:param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
|
|
@@ -2221,18 +2223,20 @@ class HTTPRunDB(RunDBInterface):
|
|
|
2221
2223
|
elif pipe_file.endswith(".zip"):
|
|
2222
2224
|
headers = {"content-type": "application/zip"}
|
|
2223
2225
|
else:
|
|
2224
|
-
raise ValueError("pipeline file must be .yaml or .zip")
|
|
2226
|
+
raise ValueError("'pipeline' file must be .yaml or .zip")
|
|
2225
2227
|
if arguments:
|
|
2226
2228
|
if not isinstance(arguments, dict):
|
|
2227
|
-
raise ValueError("arguments must be dict type")
|
|
2229
|
+
raise ValueError("'arguments' must be dict type")
|
|
2228
2230
|
headers[mlrun.common.schemas.HeaderNames.pipeline_arguments] = str(
|
|
2229
2231
|
arguments
|
|
2230
2232
|
)
|
|
2231
2233
|
|
|
2232
2234
|
if not path.isfile(pipe_file):
|
|
2233
|
-
raise OSError(f"
|
|
2235
|
+
raise OSError(f"File {pipe_file} doesnt exist")
|
|
2234
2236
|
with open(pipe_file, "rb") as fp:
|
|
2235
2237
|
data = fp.read()
|
|
2238
|
+
if not data:
|
|
2239
|
+
raise ValueError("The compiled pipe file is empty")
|
|
2236
2240
|
if not isinstance(pipeline, str):
|
|
2237
2241
|
remove(pipe_file)
|
|
2238
2242
|
|
|
@@ -3584,7 +3588,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3584
3588
|
params = {
|
|
3585
3589
|
"type": type,
|
|
3586
3590
|
"endpoint-id": endpoint_ids,
|
|
3587
|
-
"
|
|
3591
|
+
"events-format": events_format,
|
|
3588
3592
|
}
|
|
3589
3593
|
error_message = (
|
|
3590
3594
|
f"Failed to get model monitoring metrics,"
|
|
@@ -3720,7 +3724,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3720
3724
|
path=path,
|
|
3721
3725
|
body=model_endpoint.json(),
|
|
3722
3726
|
params={
|
|
3723
|
-
"
|
|
3727
|
+
"creation-strategy": creation_strategy,
|
|
3724
3728
|
},
|
|
3725
3729
|
)
|
|
3726
3730
|
return mlrun.common.schemas.ModelEndpoint(**response.json())
|
|
@@ -3750,9 +3754,9 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3750
3754
|
method=mlrun.common.types.HTTPMethod.DELETE,
|
|
3751
3755
|
path=path,
|
|
3752
3756
|
params={
|
|
3753
|
-
"
|
|
3754
|
-
"
|
|
3755
|
-
"
|
|
3757
|
+
"function-name": function_name,
|
|
3758
|
+
"function-tag": function_tag,
|
|
3759
|
+
"endpoint-id": endpoint_id,
|
|
3756
3760
|
},
|
|
3757
3761
|
)
|
|
3758
3762
|
|
|
@@ -3767,7 +3771,8 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3767
3771
|
labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
|
|
3768
3772
|
start: Optional[datetime] = None,
|
|
3769
3773
|
end: Optional[datetime] = None,
|
|
3770
|
-
tsdb_metrics: bool =
|
|
3774
|
+
tsdb_metrics: bool = False,
|
|
3775
|
+
metric_list: Optional[list[str]] = None,
|
|
3771
3776
|
top_level: bool = False,
|
|
3772
3777
|
uids: Optional[list[str]] = None,
|
|
3773
3778
|
latest_only: bool = False,
|
|
@@ -3785,6 +3790,9 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3785
3790
|
:param start: The start time to filter by.Corresponding to the `created` field.
|
|
3786
3791
|
:param end: The end time to filter by. Corresponding to the `created` field.
|
|
3787
3792
|
:param tsdb_metrics: Whether to include metrics from the time series DB.
|
|
3793
|
+
:param metric_list: List of metrics to include from the time series DB. Defaults to all metrics.
|
|
3794
|
+
If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
|
|
3795
|
+
will be included.
|
|
3788
3796
|
:param top_level: Whether to return only top level model endpoints.
|
|
3789
3797
|
:param uids: A list of unique ids to filter by.
|
|
3790
3798
|
:param latest_only: Whether to return only the latest model endpoint version.
|
|
@@ -3799,17 +3807,18 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3799
3807
|
path=path,
|
|
3800
3808
|
params={
|
|
3801
3809
|
"name": names,
|
|
3802
|
-
"
|
|
3803
|
-
"
|
|
3804
|
-
"
|
|
3805
|
-
"
|
|
3810
|
+
"model-name": model_name,
|
|
3811
|
+
"model-tag": model_tag,
|
|
3812
|
+
"function-name": function_name,
|
|
3813
|
+
"function-tag": function_tag,
|
|
3806
3814
|
"label": labels,
|
|
3807
3815
|
"start": datetime_to_iso(start),
|
|
3808
3816
|
"end": datetime_to_iso(end),
|
|
3809
|
-
"
|
|
3817
|
+
"tsdb-metrics": tsdb_metrics,
|
|
3818
|
+
"metric": metric_list,
|
|
3810
3819
|
"top-level": top_level,
|
|
3811
3820
|
"uid": uids,
|
|
3812
|
-
"
|
|
3821
|
+
"latest-only": latest_only,
|
|
3813
3822
|
},
|
|
3814
3823
|
)
|
|
3815
3824
|
|
|
@@ -3823,6 +3832,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3823
3832
|
function_tag: Optional[str] = None,
|
|
3824
3833
|
endpoint_id: Optional[str] = None,
|
|
3825
3834
|
tsdb_metrics: bool = True,
|
|
3835
|
+
metric_list: Optional[list[str]] = None,
|
|
3826
3836
|
feature_analysis: bool = False,
|
|
3827
3837
|
) -> mlrun.common.schemas.ModelEndpoint:
|
|
3828
3838
|
"""
|
|
@@ -3834,6 +3844,9 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3834
3844
|
:param function_tag: The tag of the function
|
|
3835
3845
|
:param endpoint_id: The id of the endpoint
|
|
3836
3846
|
:param tsdb_metrics: Whether to include metrics from the time series DB.
|
|
3847
|
+
:param metric_list: List of metrics to include from the time series DB. Defaults to all metrics.
|
|
3848
|
+
If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
|
|
3849
|
+
will be included.
|
|
3837
3850
|
:param feature_analysis: Whether to include feature analysis data (feature_stats,
|
|
3838
3851
|
current_stats & drift_measures).
|
|
3839
3852
|
|
|
@@ -3847,11 +3860,12 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3847
3860
|
method=mlrun.common.types.HTTPMethod.GET,
|
|
3848
3861
|
path=path,
|
|
3849
3862
|
params={
|
|
3850
|
-
"
|
|
3851
|
-
"
|
|
3852
|
-
"
|
|
3853
|
-
"
|
|
3854
|
-
"
|
|
3863
|
+
"function-name": function_name,
|
|
3864
|
+
"function-tag": function_tag,
|
|
3865
|
+
"endpoint-id": endpoint_id,
|
|
3866
|
+
"tsdb-metrics": tsdb_metrics,
|
|
3867
|
+
"metric": metric_list,
|
|
3868
|
+
"feature-analysis": feature_analysis,
|
|
3855
3869
|
},
|
|
3856
3870
|
)
|
|
3857
3871
|
|
|
@@ -5089,6 +5103,13 @@ class HTTPRunDB(RunDBInterface):
|
|
|
5089
5103
|
project = project or config.default_project
|
|
5090
5104
|
labels = self._parse_labels(labels)
|
|
5091
5105
|
|
|
5106
|
+
if limit:
|
|
5107
|
+
# TODO: Remove this in 1.11.0
|
|
5108
|
+
warnings.warn(
|
|
5109
|
+
"'limit' is deprecated and will be removed in 1.11.0. Use 'page' and 'page_size' instead.",
|
|
5110
|
+
FutureWarning,
|
|
5111
|
+
)
|
|
5112
|
+
|
|
5092
5113
|
params = {
|
|
5093
5114
|
"name": name,
|
|
5094
5115
|
"tag": tag,
|
|
@@ -5225,9 +5246,9 @@ class HTTPRunDB(RunDBInterface):
|
|
|
5225
5246
|
)
|
|
5226
5247
|
|
|
5227
5248
|
if state:
|
|
5228
|
-
# TODO: Remove this in 1.
|
|
5249
|
+
# TODO: Remove this in 1.10.0
|
|
5229
5250
|
warnings.warn(
|
|
5230
|
-
"'state' is deprecated and will be removed in 1.
|
|
5251
|
+
"'state' is deprecated in 1.7.0 and will be removed in 1.10.0. Use 'states' instead.",
|
|
5231
5252
|
FutureWarning,
|
|
5232
5253
|
)
|
|
5233
5254
|
|
mlrun/db/nopdb.py
CHANGED
|
@@ -631,7 +631,8 @@ class NopDB(RunDBInterface):
|
|
|
631
631
|
labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
|
|
632
632
|
start: Optional[datetime.datetime] = None,
|
|
633
633
|
end: Optional[datetime.datetime] = None,
|
|
634
|
-
tsdb_metrics: bool =
|
|
634
|
+
tsdb_metrics: bool = False,
|
|
635
|
+
metric_list: Optional[list[str]] = None,
|
|
635
636
|
top_level: bool = False,
|
|
636
637
|
uids: Optional[list[str]] = None,
|
|
637
638
|
latest_only: bool = False,
|
|
@@ -646,6 +647,7 @@ class NopDB(RunDBInterface):
|
|
|
646
647
|
function_tag: Optional[str] = None,
|
|
647
648
|
endpoint_id: Optional[str] = None,
|
|
648
649
|
tsdb_metrics: bool = True,
|
|
650
|
+
metric_list: Optional[list[str]] = None,
|
|
649
651
|
feature_analysis: bool = False,
|
|
650
652
|
) -> mlrun.common.schemas.ModelEndpoint:
|
|
651
653
|
pass
|
mlrun/execution.py
CHANGED
|
@@ -976,7 +976,7 @@ class MLClientCtx:
|
|
|
976
976
|
def get_cached_artifact(self, key):
|
|
977
977
|
"""Return a logged artifact from cache (for potential updates)"""
|
|
978
978
|
warnings.warn(
|
|
979
|
-
"get_cached_artifact is deprecated in 1.8.0 and will be removed in 1.
|
|
979
|
+
"get_cached_artifact is deprecated in 1.8.0 and will be removed in 1.11.0. Use get_artifact instead.",
|
|
980
980
|
FutureWarning,
|
|
981
981
|
)
|
|
982
982
|
return self.get_artifact(key)
|
|
@@ -690,10 +690,10 @@ class ModelHandler(ABC, Generic[CommonTypes.ModelType, CommonTypes.IOSampleType]
|
|
|
690
690
|
}
|
|
691
691
|
self._registered_artifacts = {}
|
|
692
692
|
|
|
693
|
-
# Get the model artifact. If the model was logged during this run, use the
|
|
693
|
+
# Get the model artifact. If the model was logged during this run, use the artifact, otherwise use the
|
|
694
694
|
# user's given model path:
|
|
695
695
|
model_artifact = (
|
|
696
|
-
self._context.
|
|
696
|
+
self._context.get_artifact(self._model_name)
|
|
697
697
|
if self._is_logged
|
|
698
698
|
else self._model_path
|
|
699
699
|
)
|
mlrun/launcher/client.py
CHANGED
|
@@ -72,7 +72,7 @@ class ClientBaseLauncher(launcher.BaseLauncher, abc.ABC):
|
|
|
72
72
|
):
|
|
73
73
|
run.metadata.labels[mlrun_constants.MLRunInternalLabels.kind] = runtime.kind
|
|
74
74
|
mlrun.runtimes.utils.enrich_run_labels(
|
|
75
|
-
run.metadata.labels, [
|
|
75
|
+
run.metadata.labels, [mlrun_constants.MLRunInternalLabels.owner]
|
|
76
76
|
)
|
|
77
77
|
if run.spec.output_path:
|
|
78
78
|
run.spec.output_path = run.spec.output_path.replace(
|
mlrun/model_monitoring/api.py
CHANGED
|
@@ -181,7 +181,7 @@ def record_results(
|
|
|
181
181
|
if drift_threshold is not None or possible_drift_threshold is not None:
|
|
182
182
|
warnings.warn(
|
|
183
183
|
"Custom drift threshold arguments are deprecated since version "
|
|
184
|
-
"1.7.0 and have no effect. They will be removed in version 1.
|
|
184
|
+
"1.7.0 and have no effect. They will be removed in version 1.10.0.\n"
|
|
185
185
|
"To enable the default histogram data drift application, run:\n"
|
|
186
186
|
"`project.enable_model_monitoring()`.",
|
|
187
187
|
FutureWarning,
|
|
@@ -189,7 +189,7 @@ def record_results(
|
|
|
189
189
|
if trigger_monitoring_job is not False:
|
|
190
190
|
warnings.warn(
|
|
191
191
|
"`trigger_monitoring_job` argument is deprecated since version "
|
|
192
|
-
"1.7.0 and has no effect. It will be removed in version 1.
|
|
192
|
+
"1.7.0 and has no effect. It will be removed in version 1.10.0.\n"
|
|
193
193
|
"To enable the default histogram data drift application, run:\n"
|
|
194
194
|
"`project.enable_model_monitoring()`.",
|
|
195
195
|
FutureWarning,
|
|
@@ -197,13 +197,13 @@ def record_results(
|
|
|
197
197
|
if artifacts_tag != "":
|
|
198
198
|
warnings.warn(
|
|
199
199
|
"`artifacts_tag` argument is deprecated since version "
|
|
200
|
-
"1.7.0 and has no effect. It will be removed in version 1.
|
|
200
|
+
"1.7.0 and has no effect. It will be removed in version 1.10.0.",
|
|
201
201
|
FutureWarning,
|
|
202
202
|
)
|
|
203
203
|
if default_batch_image != "mlrun/mlrun":
|
|
204
204
|
warnings.warn(
|
|
205
205
|
"`default_batch_image` argument is deprecated since version "
|
|
206
|
-
"1.7.0 and has no effect. It will be removed in version 1.
|
|
206
|
+
"1.7.0 and has no effect. It will be removed in version 1.10.0.",
|
|
207
207
|
FutureWarning,
|
|
208
208
|
)
|
|
209
209
|
|
|
@@ -96,7 +96,9 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
96
96
|
logger.debug(
|
|
97
97
|
"Pushing data to output stream", writer_event=str(writer_event)
|
|
98
98
|
)
|
|
99
|
-
self.output_stream.push(
|
|
99
|
+
self.output_stream.push(
|
|
100
|
+
[writer_event], partition_key=application_context.endpoint_id
|
|
101
|
+
)
|
|
100
102
|
logger.debug("Pushed data to output stream successfully")
|
|
101
103
|
|
|
102
104
|
def _lazy_init(self):
|
|
@@ -14,19 +14,18 @@
|
|
|
14
14
|
|
|
15
15
|
import json
|
|
16
16
|
import posixpath
|
|
17
|
-
import uuid
|
|
18
17
|
import warnings
|
|
19
18
|
from abc import ABC
|
|
19
|
+
from tempfile import NamedTemporaryFile
|
|
20
|
+
from typing import Optional
|
|
20
21
|
|
|
21
|
-
import pandas as pd
|
|
22
22
|
import semver
|
|
23
|
-
from evidently.ui.storage.local.base import METADATA_PATH, FSLocation
|
|
24
23
|
|
|
25
24
|
import mlrun.model_monitoring.applications.base as mm_base
|
|
26
25
|
import mlrun.model_monitoring.applications.context as mm_context
|
|
27
|
-
from mlrun.errors import MLRunIncompatibleVersionError
|
|
26
|
+
from mlrun.errors import MLRunIncompatibleVersionError, MLRunValueError
|
|
28
27
|
|
|
29
|
-
SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.
|
|
28
|
+
SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.7.5")
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
def _check_evidently_version(*, cur: semver.Version, ref: semver.Version) -> None:
|
|
@@ -60,36 +59,66 @@ except ModuleNotFoundError:
|
|
|
60
59
|
|
|
61
60
|
|
|
62
61
|
if _HAS_EVIDENTLY:
|
|
63
|
-
from evidently.
|
|
64
|
-
from evidently.ui.
|
|
65
|
-
from evidently.ui.workspace import
|
|
66
|
-
|
|
62
|
+
from evidently.core.report import Snapshot
|
|
63
|
+
from evidently.legacy.ui.storage.local.base import METADATA_PATH, FSLocation
|
|
64
|
+
from evidently.ui.workspace import (
|
|
65
|
+
STR_UUID,
|
|
66
|
+
CloudWorkspace,
|
|
67
|
+
Project,
|
|
68
|
+
Workspace,
|
|
69
|
+
WorkspaceBase,
|
|
70
|
+
)
|
|
67
71
|
|
|
68
72
|
|
|
69
73
|
class EvidentlyModelMonitoringApplicationBase(
|
|
70
74
|
mm_base.ModelMonitoringApplicationBase, ABC
|
|
71
75
|
):
|
|
72
76
|
def __init__(
|
|
73
|
-
self,
|
|
77
|
+
self,
|
|
78
|
+
evidently_project_id: "STR_UUID",
|
|
79
|
+
evidently_workspace_path: Optional[str] = None,
|
|
80
|
+
cloud_workspace: bool = False,
|
|
74
81
|
) -> None:
|
|
75
82
|
"""
|
|
76
|
-
A class for integrating Evidently for
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
A class for integrating Evidently for MLRun model monitoring within a monitoring application.
|
|
84
|
+
|
|
85
|
+
.. note::
|
|
86
|
+
|
|
87
|
+
The ``evidently`` package is not installed by default in the mlrun/mlrun image.
|
|
88
|
+
It must be installed separately to use this class.
|
|
79
89
|
|
|
80
|
-
:param evidently_workspace_path: (str) The path to the Evidently workspace.
|
|
81
90
|
:param evidently_project_id: (str) The ID of the Evidently project.
|
|
91
|
+
:param evidently_workspace_path: (str) The path to the Evidently workspace.
|
|
92
|
+
:param cloud_workspace: (bool) Whether the workspace is an Evidently Cloud workspace.
|
|
82
93
|
"""
|
|
83
|
-
|
|
84
|
-
# TODO : more then one project (mep -> project)
|
|
85
94
|
if not _HAS_EVIDENTLY:
|
|
86
95
|
raise ModuleNotFoundError("Evidently is not installed - the app cannot run")
|
|
87
|
-
self.
|
|
88
|
-
|
|
96
|
+
self.evidently_workspace_path = evidently_workspace_path
|
|
97
|
+
if cloud_workspace:
|
|
98
|
+
self.get_workspace = self.get_cloud_workspace
|
|
99
|
+
self.evidently_workspace = self.get_workspace()
|
|
89
100
|
self.evidently_project_id = evidently_project_id
|
|
90
|
-
self.evidently_project = self.
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
self.evidently_project = self.load_project()
|
|
102
|
+
|
|
103
|
+
def load_project(self) -> Project:
|
|
104
|
+
"""Load the Evidently project."""
|
|
105
|
+
return self.evidently_workspace.get_project(self.evidently_project_id)
|
|
106
|
+
|
|
107
|
+
def get_workspace(self) -> WorkspaceBase:
|
|
108
|
+
"""Get the Evidently workspace. Override this method for customize access to the workspace."""
|
|
109
|
+
if self.evidently_workspace_path:
|
|
110
|
+
self._log_location(self.evidently_workspace_path)
|
|
111
|
+
return Workspace.create(self.evidently_workspace_path)
|
|
112
|
+
else:
|
|
113
|
+
raise MLRunValueError(
|
|
114
|
+
"A local workspace could not be created as `evidently_workspace_path` is not set.\n"
|
|
115
|
+
"If you intend to use a cloud workspace, please use `cloud_workspace=True` and set the "
|
|
116
|
+
"`EVIDENTLY_API_KEY` environment variable. In other cases, override this method."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def get_cloud_workspace(self) -> CloudWorkspace:
|
|
120
|
+
"""Load the Evidently cloud workspace according to the `EVIDENTLY_API_KEY` environment variable."""
|
|
121
|
+
return CloudWorkspace()
|
|
93
122
|
|
|
94
123
|
@staticmethod
|
|
95
124
|
def _log_location(evidently_workspace_path):
|
|
@@ -128,7 +157,7 @@ class EvidentlyModelMonitoringApplicationBase(
|
|
|
128
157
|
@staticmethod
|
|
129
158
|
def log_evidently_object(
|
|
130
159
|
monitoring_context: mm_context.MonitoringApplicationContext,
|
|
131
|
-
evidently_object: "
|
|
160
|
+
evidently_object: "Snapshot",
|
|
132
161
|
artifact_name: str,
|
|
133
162
|
unique_per_endpoint: bool = True,
|
|
134
163
|
) -> None:
|
|
@@ -141,56 +170,15 @@ class EvidentlyModelMonitoringApplicationBase(
|
|
|
141
170
|
This method should be called on special occasions only.
|
|
142
171
|
|
|
143
172
|
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
|
|
144
|
-
:param evidently_object: (
|
|
145
|
-
:param artifact_name: (str) The name for the logged artifact.
|
|
146
|
-
:param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
|
|
147
|
-
set to ``False`` without changing item key will cause artifact override.
|
|
148
|
-
"""
|
|
149
|
-
evidently_object_html = evidently_object.get_html()
|
|
150
|
-
monitoring_context.log_artifact(
|
|
151
|
-
artifact_name,
|
|
152
|
-
body=evidently_object_html.encode("utf-8"),
|
|
153
|
-
format="html",
|
|
154
|
-
unique_per_endpoint=unique_per_endpoint,
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
def log_project_dashboard(
|
|
158
|
-
self,
|
|
159
|
-
monitoring_context: mm_context.MonitoringApplicationContext,
|
|
160
|
-
timestamp_start: pd.Timestamp,
|
|
161
|
-
timestamp_end: pd.Timestamp,
|
|
162
|
-
artifact_name: str = "dashboard",
|
|
163
|
-
unique_per_endpoint: bool = True,
|
|
164
|
-
) -> None:
|
|
165
|
-
"""
|
|
166
|
-
Logs an Evidently project dashboard.
|
|
167
|
-
|
|
168
|
-
.. caution::
|
|
169
|
-
|
|
170
|
-
Logging Evidently dashboards in every model monitoring window may cause scale issues.
|
|
171
|
-
This method should be called on special occasions only.
|
|
172
|
-
|
|
173
|
-
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
|
|
174
|
-
:param timestamp_start: (pd.Timestamp) The start timestamp for the dashboard data.
|
|
175
|
-
:param timestamp_end: (pd.Timestamp) The end timestamp for the dashboard data.
|
|
173
|
+
:param evidently_object: (Snapshot) The Evidently run to log, e.g. a report run.
|
|
176
174
|
:param artifact_name: (str) The name for the logged artifact.
|
|
177
175
|
:param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
|
|
178
176
|
set to ``False`` without changing item key will cause artifact override.
|
|
179
177
|
"""
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
additional_graphs={},
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
dashboard_html = file_html_template(params=template_params)
|
|
191
|
-
monitoring_context.log_artifact(
|
|
192
|
-
artifact_name,
|
|
193
|
-
body=dashboard_html.encode("utf-8"),
|
|
194
|
-
format="html",
|
|
195
|
-
unique_per_endpoint=unique_per_endpoint,
|
|
196
|
-
)
|
|
178
|
+
with NamedTemporaryFile(suffix=".html") as file:
|
|
179
|
+
evidently_object.save_html(filename=file.name)
|
|
180
|
+
monitoring_context.log_artifact(
|
|
181
|
+
artifact_name,
|
|
182
|
+
local_path=file.name,
|
|
183
|
+
unique_per_endpoint=unique_per_endpoint,
|
|
184
|
+
)
|
|
@@ -25,6 +25,7 @@ from types import TracebackType
|
|
|
25
25
|
from typing import Any, NamedTuple, Optional, Union, cast
|
|
26
26
|
|
|
27
27
|
import nuclio_sdk
|
|
28
|
+
import pandas as pd
|
|
28
29
|
|
|
29
30
|
import mlrun
|
|
30
31
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
@@ -673,7 +674,15 @@ class MonitoringApplicationController:
|
|
|
673
674
|
"""
|
|
674
675
|
logger.info("Starting monitoring controller chief")
|
|
675
676
|
applications_names = []
|
|
676
|
-
endpoints = self.project_obj.list_model_endpoints(tsdb_metrics=
|
|
677
|
+
endpoints = self.project_obj.list_model_endpoints(tsdb_metrics=False).endpoints
|
|
678
|
+
last_request_dict = self.tsdb_connector.get_last_request(
|
|
679
|
+
endpoint_ids=[mep.metadata.uid for mep in endpoints]
|
|
680
|
+
)
|
|
681
|
+
if isinstance(last_request_dict, pd.DataFrame):
|
|
682
|
+
last_request_dict = last_request_dict.set_index(
|
|
683
|
+
mm_constants.EventFieldType.ENDPOINT_ID
|
|
684
|
+
)[mm_constants.ModelEndpointSchema.LAST_REQUEST].to_dict()
|
|
685
|
+
|
|
677
686
|
if not endpoints:
|
|
678
687
|
logger.info("No model endpoints found", project=self.project)
|
|
679
688
|
return
|
|
@@ -719,16 +728,22 @@ class MonitoringApplicationController:
|
|
|
719
728
|
with schedules.ModelMonitoringSchedulesFileChief(
|
|
720
729
|
self.project
|
|
721
730
|
) as schedule_file:
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
731
|
+
for endpoint in endpoints:
|
|
732
|
+
last_request = last_request_dict.get(endpoint.metadata.uid, None)
|
|
733
|
+
if isinstance(last_request, float):
|
|
734
|
+
last_request = pd.to_datetime(last_request, unit="s", utc=True)
|
|
735
|
+
endpoint.status.last_request = (
|
|
736
|
+
last_request or endpoint.status.last_request
|
|
737
|
+
)
|
|
738
|
+
futures = {
|
|
739
|
+
pool.submit(
|
|
740
|
+
self.endpoint_to_regular_event,
|
|
741
|
+
endpoint,
|
|
742
|
+
policy,
|
|
743
|
+
set(applications_names),
|
|
744
|
+
schedule_file,
|
|
745
|
+
): endpoint
|
|
746
|
+
}
|
|
732
747
|
for future in concurrent.futures.as_completed(futures):
|
|
733
748
|
if future.exception():
|
|
734
749
|
exception = future.exception()
|
|
@@ -82,7 +82,8 @@ class TSDBConnector(ABC):
|
|
|
82
82
|
|
|
83
83
|
@abstractmethod
|
|
84
84
|
def delete_tsdb_records(
|
|
85
|
-
self,
|
|
85
|
+
self,
|
|
86
|
+
endpoint_ids: list[str],
|
|
86
87
|
) -> None:
|
|
87
88
|
"""
|
|
88
89
|
Delete model endpoint records from the TSDB connector.
|
|
@@ -332,6 +333,7 @@ class TSDBConnector(ABC):
|
|
|
332
333
|
model_endpoint_objects: list[mlrun.common.schemas.ModelEndpoint],
|
|
333
334
|
project: str,
|
|
334
335
|
run_in_threadpool: Callable,
|
|
336
|
+
metric_list: Optional[list[str]] = None,
|
|
335
337
|
) -> list[mlrun.common.schemas.ModelEndpoint]:
|
|
336
338
|
raise NotImplementedError()
|
|
337
339
|
|