mlrun 1.7.2rc3__py3-none-any.whl → 1.8.0__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 +26 -22
- mlrun/__main__.py +15 -16
- mlrun/alerts/alert.py +150 -15
- mlrun/api/schemas/__init__.py +1 -9
- mlrun/artifacts/__init__.py +2 -3
- mlrun/artifacts/base.py +62 -19
- mlrun/artifacts/dataset.py +17 -17
- mlrun/artifacts/document.py +454 -0
- mlrun/artifacts/manager.py +28 -18
- mlrun/artifacts/model.py +91 -59
- mlrun/artifacts/plots.py +2 -2
- mlrun/common/constants.py +8 -0
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/artifact.py +1 -1
- mlrun/common/formatters/feature_set.py +2 -0
- mlrun/common/formatters/function.py +1 -0
- mlrun/{model_monitoring/db/stores/v3io_kv/__init__.py → common/formatters/model_endpoint.py} +17 -0
- 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 +12 -62
- mlrun/common/runtimes/constants.py +25 -4
- mlrun/common/schemas/__init__.py +9 -5
- mlrun/common/schemas/alert.py +114 -19
- mlrun/common/schemas/api_gateway.py +3 -3
- mlrun/common/schemas/artifact.py +22 -9
- mlrun/common/schemas/auth.py +8 -4
- mlrun/common/schemas/background_task.py +7 -7
- mlrun/common/schemas/client_spec.py +4 -4
- 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 +4 -8
- mlrun/common/schemas/model_monitoring/constants.py +127 -46
- mlrun/common/schemas/model_monitoring/grafana.py +18 -12
- mlrun/common/schemas/model_monitoring/model_endpoints.py +154 -160
- 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 +142 -0
- mlrun/common/schemas/pipeline.py +3 -3
- mlrun/common/schemas/project.py +26 -18
- mlrun/common/schemas/runs.py +3 -3
- mlrun/common/schemas/runtime_resource.py +5 -5
- mlrun/common/schemas/schedule.py +1 -1
- mlrun/common/schemas/secret.py +1 -1
- mlrun/{model_monitoring/db/stores/sqldb/__init__.py → common/schemas/serving.py} +10 -1
- mlrun/common/schemas/tag.py +3 -3
- mlrun/common/schemas/workflow.py +6 -5
- mlrun/common/types.py +1 -0
- mlrun/config.py +157 -89
- mlrun/data_types/__init__.py +5 -3
- mlrun/data_types/infer.py +13 -3
- mlrun/data_types/spark.py +2 -1
- mlrun/datastore/__init__.py +59 -18
- mlrun/datastore/alibaba_oss.py +4 -1
- mlrun/datastore/azure_blob.py +4 -1
- mlrun/datastore/base.py +19 -24
- mlrun/datastore/datastore.py +10 -4
- mlrun/datastore/datastore_profile.py +178 -45
- 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 +14 -3
- mlrun/datastore/sources.py +89 -92
- mlrun/datastore/store_resources.py +7 -4
- mlrun/datastore/storeytargets.py +51 -16
- mlrun/datastore/targets.py +38 -31
- mlrun/datastore/utils.py +87 -4
- mlrun/datastore/v3io.py +4 -1
- mlrun/datastore/vectorstore.py +291 -0
- mlrun/datastore/wasbfs/fs.py +13 -12
- mlrun/db/base.py +286 -100
- mlrun/db/httpdb.py +1562 -490
- mlrun/db/nopdb.py +250 -83
- mlrun/errors.py +6 -2
- mlrun/execution.py +194 -50
- mlrun/feature_store/__init__.py +2 -10
- mlrun/feature_store/api.py +20 -458
- mlrun/feature_store/common.py +9 -9
- mlrun/feature_store/feature_set.py +20 -18
- mlrun/feature_store/feature_vector.py +105 -479
- mlrun/feature_store/feature_vector_utils.py +466 -0
- mlrun/feature_store/retrieval/base.py +15 -11
- mlrun/feature_store/retrieval/job.py +2 -1
- mlrun/feature_store/retrieval/storey_merger.py +1 -1
- mlrun/feature_store/steps.py +3 -3
- 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 +31 -31
- 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/k8s_utils.py +2 -5
- mlrun/launcher/base.py +3 -4
- mlrun/launcher/client.py +2 -2
- mlrun/launcher/local.py +6 -2
- mlrun/launcher/remote.py +1 -1
- mlrun/lists.py +8 -4
- mlrun/model.py +132 -46
- mlrun/model_monitoring/__init__.py +3 -5
- mlrun/model_monitoring/api.py +113 -98
- mlrun/model_monitoring/applications/__init__.py +0 -5
- mlrun/model_monitoring/applications/_application_steps.py +81 -50
- mlrun/model_monitoring/applications/base.py +467 -14
- mlrun/model_monitoring/applications/context.py +212 -134
- mlrun/model_monitoring/{db/stores/base → applications/evidently}/__init__.py +6 -2
- mlrun/model_monitoring/applications/evidently/base.py +146 -0
- mlrun/model_monitoring/applications/histogram_data_drift.py +89 -56
- mlrun/model_monitoring/applications/results.py +67 -15
- mlrun/model_monitoring/controller.py +701 -315
- mlrun/model_monitoring/db/__init__.py +0 -2
- mlrun/model_monitoring/db/_schedules.py +242 -0
- mlrun/model_monitoring/db/_stats.py +189 -0
- mlrun/model_monitoring/db/tsdb/__init__.py +33 -22
- mlrun/model_monitoring/db/tsdb/base.py +243 -49
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +76 -36
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +213 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +534 -88
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +436 -106
- mlrun/model_monitoring/helpers.py +356 -114
- mlrun/model_monitoring/stream_processing.py +190 -345
- mlrun/model_monitoring/tracking_policy.py +11 -4
- mlrun/model_monitoring/writer.py +49 -90
- mlrun/package/__init__.py +3 -6
- mlrun/package/context_handler.py +2 -2
- 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 +35 -32
- 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 +30 -30
- mlrun/projects/pipelines.py +116 -47
- mlrun/projects/project.py +1292 -329
- mlrun/render.py +5 -9
- mlrun/run.py +57 -14
- mlrun/runtimes/__init__.py +1 -3
- mlrun/runtimes/base.py +30 -22
- mlrun/runtimes/daskjob.py +9 -9
- mlrun/runtimes/databricks_job/databricks_runtime.py +6 -5
- mlrun/runtimes/function_reference.py +5 -2
- mlrun/runtimes/generators.py +3 -2
- mlrun/runtimes/kubejob.py +6 -7
- mlrun/runtimes/mounts.py +574 -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 -13
- mlrun/runtimes/nuclio/application/reverse_proxy.go +66 -64
- mlrun/runtimes/nuclio/function.py +127 -70
- mlrun/runtimes/nuclio/serving.py +105 -37
- mlrun/runtimes/pod.py +159 -54
- mlrun/runtimes/remotesparkjob.py +3 -2
- mlrun/runtimes/sparkjob/__init__.py +0 -2
- mlrun/runtimes/sparkjob/spark3job.py +22 -12
- mlrun/runtimes/utils.py +7 -6
- mlrun/secrets.py +2 -2
- mlrun/serving/__init__.py +8 -0
- mlrun/serving/merger.py +7 -5
- mlrun/serving/remote.py +35 -22
- mlrun/serving/routers.py +186 -240
- mlrun/serving/server.py +41 -10
- mlrun/serving/states.py +432 -118
- mlrun/serving/utils.py +13 -2
- mlrun/serving/v1_serving.py +3 -2
- mlrun/serving/v2_serving.py +161 -203
- 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 +35 -22
- mlrun/utils/clones.py +7 -4
- mlrun/utils/helpers.py +511 -58
- mlrun/utils/logger.py +119 -13
- mlrun/utils/notifications/notification/__init__.py +22 -19
- mlrun/utils/notifications/notification/base.py +39 -15
- 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 +16 -8
- mlrun/utils/notifications/notification/webhook.py +24 -8
- mlrun/utils/notifications/notification_pusher.py +191 -200
- mlrun/utils/regex.py +12 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0.dist-info}/METADATA +81 -54
- mlrun-1.8.0.dist-info/RECORD +351 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0.dist-info}/WHEEL +1 -1
- mlrun/model_monitoring/applications/evidently_base.py +0 -137
- 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/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/kv_store.py +0 -726
- mlrun/model_monitoring/model_endpoint.py +0 -118
- mlrun-1.7.2rc3.dist-info/RECORD +0 -351
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0.dist-info/licenses}/LICENSE +0 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0.dist-info}/top_level.txt +0 -0
|
@@ -12,84 +12,92 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
import json
|
|
16
15
|
import socket
|
|
17
|
-
from typing import Any, Optional, cast
|
|
16
|
+
from typing import Any, Optional, Protocol, cast
|
|
18
17
|
|
|
18
|
+
import nuclio.request
|
|
19
19
|
import numpy as np
|
|
20
20
|
import pandas as pd
|
|
21
21
|
|
|
22
22
|
import mlrun.common.constants as mlrun_constants
|
|
23
23
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
24
|
+
import mlrun.errors
|
|
24
25
|
import mlrun.feature_store as fstore
|
|
26
|
+
import mlrun.feature_store.feature_set as fs
|
|
25
27
|
import mlrun.features
|
|
26
28
|
import mlrun.serving
|
|
27
29
|
import mlrun.utils
|
|
28
30
|
from mlrun.artifacts import Artifact, DatasetArtifact, ModelArtifact, get_model
|
|
29
|
-
from mlrun.common.model_monitoring.helpers import FeatureStats
|
|
31
|
+
from mlrun.common.model_monitoring.helpers import FeatureStats
|
|
32
|
+
from mlrun.common.schemas import ModelEndpoint
|
|
30
33
|
from mlrun.model_monitoring.helpers import (
|
|
31
34
|
calculate_inputs_statistics,
|
|
32
|
-
get_endpoint_record,
|
|
33
35
|
)
|
|
34
|
-
from mlrun.model_monitoring.model_endpoint import ModelEndpoint
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
class
|
|
38
|
+
class _ArtifactsLogger(Protocol):
|
|
38
39
|
"""
|
|
39
|
-
|
|
40
|
-
and also it can be used for logging artifacts and results.
|
|
41
|
-
The monitoring context has the following attributes:
|
|
42
|
-
|
|
43
|
-
:param application_name: (str) The model monitoring application name.
|
|
44
|
-
:param project_name: (str) The project name.
|
|
45
|
-
:param project: (MlrunProject) The project object.
|
|
46
|
-
:param logger: (mlrun.utils.Logger) MLRun logger.
|
|
47
|
-
:param nuclio_logger: (nuclio.request.Logger) Nuclio logger.
|
|
48
|
-
:param sample_df_stats: (FeatureStats) The new sample distribution dictionary.
|
|
49
|
-
:param feature_stats: (FeatureStats) The train sample distribution dictionary.
|
|
50
|
-
:param sample_df: (pd.DataFrame) The new sample DataFrame.
|
|
51
|
-
:param start_infer_time: (pd.Timestamp) Start time of the monitoring schedule.
|
|
52
|
-
:param end_infer_time: (pd.Timestamp) End time of the monitoring schedule.
|
|
53
|
-
:param latest_request: (pd.Timestamp) Timestamp of the latest request on this endpoint_id.
|
|
54
|
-
:param endpoint_id: (str) ID of the monitored model endpoint
|
|
55
|
-
:param output_stream_uri: (str) URI of the output stream for results
|
|
56
|
-
:param model_endpoint: (ModelEndpoint) The model endpoint object.
|
|
57
|
-
:param feature_names: (list[str]) List of models feature names.
|
|
58
|
-
:param label_names: (list[str]) List of models label names.
|
|
59
|
-
:param model: (tuple[str, ModelArtifact, dict]) The model file, model spec object,
|
|
60
|
-
and a list of extra data items.
|
|
40
|
+
Classes that implement this protocol are :code:`MlrunProject` and :code:`MLClientCtx`.
|
|
61
41
|
"""
|
|
62
42
|
|
|
43
|
+
def log_artifact(self, *args, **kwargs) -> Artifact: ...
|
|
44
|
+
def log_dataset(self, *args, **kwargs) -> DatasetArtifact: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MonitoringApplicationContext:
|
|
48
|
+
_logger_name = "monitoring-application"
|
|
49
|
+
|
|
63
50
|
def __init__(
|
|
64
51
|
self,
|
|
65
52
|
*,
|
|
66
|
-
graph_context: mlrun.serving.GraphContext,
|
|
67
53
|
application_name: str,
|
|
68
54
|
event: dict[str, Any],
|
|
69
|
-
|
|
55
|
+
project: "mlrun.MlrunProject",
|
|
56
|
+
artifacts_logger: _ArtifactsLogger,
|
|
57
|
+
logger: mlrun.utils.Logger,
|
|
58
|
+
nuclio_logger: nuclio.request.Logger,
|
|
59
|
+
model_endpoint_dict: Optional[dict[str, ModelEndpoint]] = None,
|
|
60
|
+
sample_df: Optional[pd.DataFrame] = None,
|
|
61
|
+
feature_stats: Optional[FeatureStats] = None,
|
|
62
|
+
feature_sets_dict: Optional[dict[str, fs.FeatureSet]] = None,
|
|
70
63
|
) -> None:
|
|
71
64
|
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
:param
|
|
77
|
-
:param
|
|
65
|
+
The :code:`MonitoringApplicationContext` object holds all the relevant information for the
|
|
66
|
+
model monitoring application, and can be used for logging artifacts and messages.
|
|
67
|
+
The monitoring context has the following attributes:
|
|
68
|
+
|
|
69
|
+
:param application_name: (str) The model monitoring application name.
|
|
70
|
+
:param project: (:py:class:`~mlrun.projects.MlrunProject`) The current MLRun project object.
|
|
71
|
+
:param project_name: (str) The project name.
|
|
72
|
+
:param logger: (:py:class:`~mlrun.utils.Logger`) MLRun logger.
|
|
73
|
+
:param nuclio_logger: (nuclio.request.Logger) Nuclio logger.
|
|
74
|
+
:param sample_df_stats: (FeatureStats) The new sample distribution dictionary.
|
|
75
|
+
:param feature_stats: (FeatureStats) The train sample distribution dictionary.
|
|
76
|
+
:param sample_df: (pd.DataFrame) The new sample DataFrame.
|
|
77
|
+
:param start_infer_time: (pd.Timestamp) Start time of the monitoring schedule.
|
|
78
|
+
:param end_infer_time: (pd.Timestamp) End time of the monitoring schedule.
|
|
79
|
+
:param endpoint_id: (str) ID of the monitored model endpoint
|
|
80
|
+
:param feature_set: (FeatureSet) the model endpoint feature set
|
|
81
|
+
:param endpoint_name: (str) Name of the monitored model endpoint
|
|
82
|
+
:param output_stream_uri: (str) URI of the output stream for results
|
|
83
|
+
:param model_endpoint: (ModelEndpoint) The model endpoint object.
|
|
84
|
+
:param feature_names: (list[str]) List of models feature names.
|
|
85
|
+
:param label_names: (list[str]) List of models label names.
|
|
86
|
+
:param model: (tuple[str, ModelArtifact, dict]) The model file, model spec object,
|
|
87
|
+
and a list of extra data items.
|
|
78
88
|
"""
|
|
79
89
|
self.application_name = application_name
|
|
80
90
|
|
|
81
|
-
self.
|
|
82
|
-
self.
|
|
91
|
+
self.project = project
|
|
92
|
+
self.project_name = project.name
|
|
93
|
+
|
|
94
|
+
self._artifacts_logger = artifacts_logger
|
|
83
95
|
|
|
84
96
|
# MLRun Logger
|
|
85
|
-
self.logger =
|
|
86
|
-
level=mlrun.mlconf.log_level,
|
|
87
|
-
formatter_kind=mlrun.mlconf.log_formatter,
|
|
88
|
-
name="monitoring-application",
|
|
89
|
-
)
|
|
97
|
+
self.logger = logger
|
|
90
98
|
# Nuclio logger - `nuclio.request.Logger`.
|
|
91
|
-
# Note: this logger
|
|
92
|
-
self.nuclio_logger =
|
|
99
|
+
# Note: this logger accepts keyword arguments only in its `_with` methods, e.g. `info_with`.
|
|
100
|
+
self.nuclio_logger = nuclio_logger
|
|
93
101
|
|
|
94
102
|
# event data
|
|
95
103
|
self.start_infer_time = pd.Timestamp(
|
|
@@ -101,29 +109,101 @@ class MonitoringApplicationContext:
|
|
|
101
109
|
self.endpoint_id = cast(
|
|
102
110
|
str, event.get(mm_constants.ApplicationEvent.ENDPOINT_ID)
|
|
103
111
|
)
|
|
104
|
-
self.
|
|
105
|
-
str, event.get(mm_constants.ApplicationEvent.
|
|
112
|
+
self.endpoint_name = cast(
|
|
113
|
+
str, event.get(mm_constants.ApplicationEvent.ENDPOINT_NAME)
|
|
106
114
|
)
|
|
107
115
|
|
|
108
|
-
self._feature_stats: Optional[FeatureStats] =
|
|
116
|
+
self._feature_stats: Optional[FeatureStats] = feature_stats
|
|
109
117
|
self._sample_df_stats: Optional[FeatureStats] = None
|
|
110
118
|
|
|
111
119
|
# Default labels for the artifacts
|
|
112
120
|
self._default_labels = self._get_default_labels()
|
|
113
121
|
|
|
114
122
|
# Persistent data - fetched when needed
|
|
115
|
-
self._sample_df: Optional[pd.DataFrame] =
|
|
116
|
-
self._model_endpoint: Optional[ModelEndpoint] =
|
|
117
|
-
self.endpoint_id
|
|
123
|
+
self._sample_df: Optional[pd.DataFrame] = sample_df
|
|
124
|
+
self._model_endpoint: Optional[ModelEndpoint] = (
|
|
125
|
+
model_endpoint_dict.get(self.endpoint_id) if model_endpoint_dict else None
|
|
126
|
+
)
|
|
127
|
+
self._feature_set: Optional[fs.FeatureSet] = (
|
|
128
|
+
feature_sets_dict.get(self.endpoint_id) if feature_sets_dict else None
|
|
129
|
+
)
|
|
130
|
+
store, _, _ = mlrun.store_manager.get_or_create_store(
|
|
131
|
+
mlrun.mlconf.artifact_path
|
|
132
|
+
)
|
|
133
|
+
self.storage_options = store.get_storage_options()
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def _from_ml_ctx(
|
|
137
|
+
cls,
|
|
138
|
+
context: "mlrun.MLClientCtx",
|
|
139
|
+
*,
|
|
140
|
+
application_name: str,
|
|
141
|
+
event: dict[str, Any],
|
|
142
|
+
model_endpoint_dict: Optional[dict[str, ModelEndpoint]] = None,
|
|
143
|
+
sample_df: Optional[pd.DataFrame] = None,
|
|
144
|
+
feature_stats: Optional[FeatureStats] = None,
|
|
145
|
+
) -> "MonitoringApplicationContext":
|
|
146
|
+
project = context.get_project_object()
|
|
147
|
+
if not project:
|
|
148
|
+
raise mlrun.errors.MLRunValueError("Could not load project from context")
|
|
149
|
+
logger = context.logger
|
|
150
|
+
artifacts_logger = context
|
|
151
|
+
nuclio_logger = nuclio.request.Logger(
|
|
152
|
+
level=mlrun.mlconf.log_level, name=cls._logger_name
|
|
153
|
+
)
|
|
154
|
+
return cls(
|
|
155
|
+
application_name=application_name,
|
|
156
|
+
event=event,
|
|
157
|
+
model_endpoint_dict=model_endpoint_dict,
|
|
158
|
+
project=project,
|
|
159
|
+
logger=logger,
|
|
160
|
+
nuclio_logger=nuclio_logger,
|
|
161
|
+
artifacts_logger=artifacts_logger,
|
|
162
|
+
sample_df=sample_df,
|
|
163
|
+
feature_stats=feature_stats,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def _from_graph_ctx(
|
|
168
|
+
cls,
|
|
169
|
+
graph_context: mlrun.serving.GraphContext,
|
|
170
|
+
*,
|
|
171
|
+
application_name: str,
|
|
172
|
+
event: dict[str, Any],
|
|
173
|
+
model_endpoint_dict: Optional[dict[str, ModelEndpoint]] = None,
|
|
174
|
+
sample_df: Optional[pd.DataFrame] = None,
|
|
175
|
+
feature_stats: Optional[FeatureStats] = None,
|
|
176
|
+
feature_sets_dict: Optional[dict[str, fs.FeatureSet]] = None,
|
|
177
|
+
) -> "MonitoringApplicationContext":
|
|
178
|
+
nuclio_logger = graph_context.logger
|
|
179
|
+
artifacts_logger = graph_context.project_obj
|
|
180
|
+
logger = mlrun.utils.create_logger(
|
|
181
|
+
level=mlrun.mlconf.log_level,
|
|
182
|
+
formatter_kind=mlrun.mlconf.log_formatter,
|
|
183
|
+
name=cls._logger_name,
|
|
184
|
+
)
|
|
185
|
+
return cls(
|
|
186
|
+
application_name=application_name,
|
|
187
|
+
event=event,
|
|
188
|
+
project=graph_context.project_obj,
|
|
189
|
+
model_endpoint_dict=model_endpoint_dict,
|
|
190
|
+
logger=logger,
|
|
191
|
+
nuclio_logger=nuclio_logger,
|
|
192
|
+
artifacts_logger=artifacts_logger,
|
|
193
|
+
sample_df=sample_df,
|
|
194
|
+
feature_stats=feature_stats,
|
|
195
|
+
feature_sets_dict=feature_sets_dict,
|
|
118
196
|
)
|
|
119
197
|
|
|
120
198
|
def _get_default_labels(self) -> dict[str, str]:
|
|
121
|
-
|
|
199
|
+
labels = {
|
|
122
200
|
mlrun_constants.MLRunInternalLabels.runner_pod: socket.gethostname(),
|
|
123
201
|
mlrun_constants.MLRunInternalLabels.producer_type: "model-monitoring-app",
|
|
124
202
|
mlrun_constants.MLRunInternalLabels.app_name: self.application_name,
|
|
125
203
|
mlrun_constants.MLRunInternalLabels.endpoint_id: self.endpoint_id,
|
|
204
|
+
mlrun_constants.MLRunInternalLabels.endpoint_name: self.endpoint_name,
|
|
126
205
|
}
|
|
206
|
+
return {key: value for key, value in labels.items() if value is not None}
|
|
127
207
|
|
|
128
208
|
def _add_default_labels(self, labels: Optional[dict[str, str]]) -> dict[str, str]:
|
|
129
209
|
"""Add the default labels to logged artifacts labels"""
|
|
@@ -132,39 +212,60 @@ class MonitoringApplicationContext:
|
|
|
132
212
|
@property
|
|
133
213
|
def sample_df(self) -> pd.DataFrame:
|
|
134
214
|
if self._sample_df is None:
|
|
135
|
-
|
|
136
|
-
self.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
215
|
+
if (
|
|
216
|
+
self.endpoint_name is None
|
|
217
|
+
or self.endpoint_id is None
|
|
218
|
+
or pd.isnull(self.start_infer_time)
|
|
219
|
+
or pd.isnull(self.end_infer_time)
|
|
220
|
+
):
|
|
221
|
+
raise mlrun.errors.MLRunValueError(
|
|
222
|
+
"You have tried to access `monitoring_context.sample_df`, but have not provided it directly "
|
|
223
|
+
"through `sample_data`, nor have you provided the model endpoint's name, ID, and the start and "
|
|
224
|
+
f"end times: `endpoint_name`={self.endpoint_name}, `endpoint_uid`={self.endpoint_id}, "
|
|
225
|
+
f"`start`={self.start_infer_time}, and `end`={self.end_infer_time}. "
|
|
226
|
+
"You can either provide the sample dataframe directly, the model endpoint's details and times, "
|
|
227
|
+
"or adapt the application's logic to not access the sample dataframe."
|
|
228
|
+
)
|
|
229
|
+
df = self.feature_set.to_dataframe(
|
|
148
230
|
start_time=self.start_infer_time,
|
|
149
231
|
end_time=self.end_infer_time,
|
|
150
|
-
|
|
232
|
+
time_column=mm_constants.EventFieldType.TIMESTAMP,
|
|
233
|
+
storage_options=self.storage_options,
|
|
151
234
|
)
|
|
152
|
-
self._sample_df =
|
|
235
|
+
self._sample_df = df.reset_index(drop=True)
|
|
153
236
|
return self._sample_df
|
|
154
237
|
|
|
155
238
|
@property
|
|
156
239
|
def model_endpoint(self) -> ModelEndpoint:
|
|
157
240
|
if not self._model_endpoint:
|
|
158
|
-
self.
|
|
159
|
-
|
|
241
|
+
if self.endpoint_name is None or self.endpoint_id is None:
|
|
242
|
+
raise mlrun.errors.MLRunValueError(
|
|
243
|
+
"You have NOT provided the model endpoint's name and ID: "
|
|
244
|
+
f"`endpoint_name`={self.endpoint_name} and `endpoint_id`={self.endpoint_id}, "
|
|
245
|
+
"but you have tried to access `monitoring_context.model_endpoint` "
|
|
246
|
+
"directly or indirectly in your application. You can either provide them, "
|
|
247
|
+
"or adapt the application's logic to not access the model endpoint."
|
|
248
|
+
)
|
|
249
|
+
self._model_endpoint = mlrun.db.get_run_db().get_model_endpoint(
|
|
250
|
+
name=self.endpoint_name,
|
|
251
|
+
project=self.project_name,
|
|
252
|
+
endpoint_id=self.endpoint_id,
|
|
253
|
+
feature_analysis=True,
|
|
160
254
|
)
|
|
161
255
|
return self._model_endpoint
|
|
162
256
|
|
|
257
|
+
@property
|
|
258
|
+
def feature_set(self) -> fs.FeatureSet:
|
|
259
|
+
if not self._feature_set and self.model_endpoint:
|
|
260
|
+
self._feature_set = fstore.get_feature_set(
|
|
261
|
+
self.model_endpoint.spec.monitoring_feature_set_uri
|
|
262
|
+
)
|
|
263
|
+
return self._feature_set
|
|
264
|
+
|
|
163
265
|
@property
|
|
164
266
|
def feature_stats(self) -> FeatureStats:
|
|
165
267
|
if not self._feature_stats:
|
|
166
|
-
self._feature_stats =
|
|
167
|
-
pad_features_hist(self._feature_stats)
|
|
268
|
+
self._feature_stats = self.model_endpoint.spec.feature_stats
|
|
168
269
|
return self._feature_stats
|
|
169
270
|
|
|
170
271
|
@property
|
|
@@ -179,18 +280,12 @@ class MonitoringApplicationContext:
|
|
|
179
280
|
@property
|
|
180
281
|
def feature_names(self) -> list[str]:
|
|
181
282
|
"""The feature names of the model"""
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
feature_names
|
|
185
|
-
if isinstance(feature_names, list)
|
|
186
|
-
else json.loads(feature_names)
|
|
187
|
-
)
|
|
283
|
+
return self.model_endpoint.spec.feature_names
|
|
188
284
|
|
|
189
285
|
@property
|
|
190
286
|
def label_names(self) -> list[str]:
|
|
191
287
|
"""The label names of the model"""
|
|
192
|
-
|
|
193
|
-
return label_names if isinstance(label_names, list) else json.loads(label_names)
|
|
288
|
+
return self.model_endpoint.spec.label_names
|
|
194
289
|
|
|
195
290
|
@property
|
|
196
291
|
def model(self) -> tuple[str, ModelArtifact, dict]:
|
|
@@ -230,14 +325,32 @@ class MonitoringApplicationContext:
|
|
|
230
325
|
upload: Optional[bool] = None,
|
|
231
326
|
labels: Optional[dict[str, str]] = None,
|
|
232
327
|
target_path: Optional[str] = None,
|
|
328
|
+
unique_per_endpoint: bool = True,
|
|
233
329
|
**kwargs,
|
|
234
330
|
) -> Artifact:
|
|
235
331
|
"""
|
|
236
332
|
Log an artifact.
|
|
237
|
-
|
|
333
|
+
|
|
334
|
+
.. caution::
|
|
335
|
+
|
|
336
|
+
Logging artifacts in every model monitoring window may cause scale issues.
|
|
337
|
+
This method should be called on special occasions only.
|
|
338
|
+
|
|
339
|
+
See :func:`~mlrun.projects.MlrunProject.log_artifact` for the full documentation, except for one
|
|
340
|
+
new argument:
|
|
341
|
+
|
|
342
|
+
:param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
|
|
343
|
+
set to ``False`` without changing item key will cause artifact override.
|
|
238
344
|
"""
|
|
239
345
|
labels = self._add_default_labels(labels)
|
|
240
|
-
|
|
346
|
+
# By default, we want to log different artifact for each model endpoint
|
|
347
|
+
endpoint_id = labels.get(mlrun_constants.MLRunInternalLabels.endpoint_id, "")
|
|
348
|
+
if unique_per_endpoint and isinstance(item, str):
|
|
349
|
+
item = f"{item}-{endpoint_id}" if endpoint_id else item
|
|
350
|
+
elif unique_per_endpoint: # isinstance(item, Artifact) is True
|
|
351
|
+
item.key = f"{item.key}-{endpoint_id}" if endpoint_id else item.key
|
|
352
|
+
|
|
353
|
+
return self._artifacts_logger.log_artifact(
|
|
241
354
|
item,
|
|
242
355
|
body=body,
|
|
243
356
|
tag=tag,
|
|
@@ -265,14 +378,30 @@ class MonitoringApplicationContext:
|
|
|
265
378
|
target_path="",
|
|
266
379
|
extra_data=None,
|
|
267
380
|
label_column: Optional[str] = None,
|
|
381
|
+
unique_per_endpoint: bool = True,
|
|
268
382
|
**kwargs,
|
|
269
383
|
) -> DatasetArtifact:
|
|
270
384
|
"""
|
|
271
385
|
Log a dataset artifact.
|
|
272
|
-
|
|
386
|
+
|
|
387
|
+
.. caution::
|
|
388
|
+
|
|
389
|
+
Logging datasets in every model monitoring window may cause scale issues.
|
|
390
|
+
This method should be called on special occasions only.
|
|
391
|
+
|
|
392
|
+
See :func:`~mlrun.projects.MlrunProject.log_dataset` for the full documentation, except for one
|
|
393
|
+
new argument:
|
|
394
|
+
|
|
395
|
+
:param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
|
|
396
|
+
set to ``False`` without changing item key will cause artifact override.
|
|
273
397
|
"""
|
|
274
398
|
labels = self._add_default_labels(labels)
|
|
275
|
-
|
|
399
|
+
# By default, we want to log different artifact for each model endpoint
|
|
400
|
+
endpoint_id = labels.get(mlrun_constants.MLRunInternalLabels.endpoint_id, "")
|
|
401
|
+
if unique_per_endpoint and isinstance(key, str):
|
|
402
|
+
key = f"{key}-{endpoint_id}" if endpoint_id else key
|
|
403
|
+
|
|
404
|
+
return self._artifacts_logger.log_dataset(
|
|
276
405
|
key,
|
|
277
406
|
df,
|
|
278
407
|
tag=tag,
|
|
@@ -288,54 +417,3 @@ class MonitoringApplicationContext:
|
|
|
288
417
|
label_column=label_column,
|
|
289
418
|
**kwargs,
|
|
290
419
|
)
|
|
291
|
-
|
|
292
|
-
def log_model(
|
|
293
|
-
self,
|
|
294
|
-
key,
|
|
295
|
-
body=None,
|
|
296
|
-
framework="",
|
|
297
|
-
tag="",
|
|
298
|
-
model_dir=None,
|
|
299
|
-
model_file=None,
|
|
300
|
-
algorithm=None,
|
|
301
|
-
metrics=None,
|
|
302
|
-
parameters=None,
|
|
303
|
-
artifact_path=None,
|
|
304
|
-
upload=None,
|
|
305
|
-
labels=None,
|
|
306
|
-
inputs: Optional[list[mlrun.features.Feature]] = None,
|
|
307
|
-
outputs: Optional[list[mlrun.features.Feature]] = None,
|
|
308
|
-
feature_vector: Optional[str] = None,
|
|
309
|
-
feature_weights: Optional[list] = None,
|
|
310
|
-
training_set=None,
|
|
311
|
-
label_column=None,
|
|
312
|
-
extra_data=None,
|
|
313
|
-
**kwargs,
|
|
314
|
-
) -> ModelArtifact:
|
|
315
|
-
"""
|
|
316
|
-
Log a model artifact.
|
|
317
|
-
See :func:`~mlrun.projects.MlrunProject.log_model` for the documentation.
|
|
318
|
-
"""
|
|
319
|
-
labels = self._add_default_labels(labels)
|
|
320
|
-
return self.project.log_model(
|
|
321
|
-
key,
|
|
322
|
-
body=body,
|
|
323
|
-
framework=framework,
|
|
324
|
-
tag=tag,
|
|
325
|
-
model_dir=model_dir,
|
|
326
|
-
model_file=model_file,
|
|
327
|
-
algorithm=algorithm,
|
|
328
|
-
metrics=metrics,
|
|
329
|
-
parameters=parameters,
|
|
330
|
-
artifact_path=artifact_path,
|
|
331
|
-
upload=upload,
|
|
332
|
-
labels=labels,
|
|
333
|
-
inputs=inputs,
|
|
334
|
-
outputs=outputs,
|
|
335
|
-
feature_vector=feature_vector,
|
|
336
|
-
feature_weights=feature_weights,
|
|
337
|
-
training_set=training_set,
|
|
338
|
-
label_column=label_column,
|
|
339
|
-
extra_data=extra_data,
|
|
340
|
-
**kwargs,
|
|
341
|
-
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 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.
|
|
@@ -12,4 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from .
|
|
15
|
+
from .base import (
|
|
16
|
+
_HAS_EVIDENTLY,
|
|
17
|
+
SUPPORTED_EVIDENTLY_VERSION,
|
|
18
|
+
EvidentlyModelMonitoringApplicationBase,
|
|
19
|
+
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Copyright 2023 Iguazio
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import warnings
|
|
16
|
+
from abc import ABC
|
|
17
|
+
from tempfile import NamedTemporaryFile
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
import semver
|
|
21
|
+
|
|
22
|
+
import mlrun.model_monitoring.applications.base as mm_base
|
|
23
|
+
import mlrun.model_monitoring.applications.context as mm_context
|
|
24
|
+
from mlrun.errors import MLRunIncompatibleVersionError, MLRunValueError
|
|
25
|
+
|
|
26
|
+
SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.7.5")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _check_evidently_version(*, cur: semver.Version, ref: semver.Version) -> None:
|
|
30
|
+
if ref.is_compatible(cur) or (
|
|
31
|
+
cur.major == ref.major == 0 and cur.minor == ref.minor and cur.patch > ref.patch
|
|
32
|
+
):
|
|
33
|
+
return
|
|
34
|
+
if cur.major == ref.major == 0 and cur.minor > ref.minor:
|
|
35
|
+
warnings.warn(
|
|
36
|
+
f"Evidently version {cur} is not compatible with the tested "
|
|
37
|
+
f"version {ref}, use at your own risk."
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
raise MLRunIncompatibleVersionError(
|
|
41
|
+
f"Evidently version {cur} is not supported, please change to "
|
|
42
|
+
f"{ref} (or another compatible version)."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
_HAS_EVIDENTLY = False
|
|
47
|
+
try:
|
|
48
|
+
import evidently # noqa: F401
|
|
49
|
+
|
|
50
|
+
_check_evidently_version(
|
|
51
|
+
cur=semver.Version.parse(evidently.__version__),
|
|
52
|
+
ref=SUPPORTED_EVIDENTLY_VERSION,
|
|
53
|
+
)
|
|
54
|
+
_HAS_EVIDENTLY = True
|
|
55
|
+
except ModuleNotFoundError:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if _HAS_EVIDENTLY:
|
|
60
|
+
from evidently.core.report import Snapshot
|
|
61
|
+
from evidently.ui.workspace import (
|
|
62
|
+
STR_UUID,
|
|
63
|
+
CloudWorkspace,
|
|
64
|
+
Project,
|
|
65
|
+
Workspace,
|
|
66
|
+
WorkspaceBase,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class EvidentlyModelMonitoringApplicationBase(
|
|
71
|
+
mm_base.ModelMonitoringApplicationBase, ABC
|
|
72
|
+
):
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
evidently_project_id: "STR_UUID",
|
|
76
|
+
evidently_workspace_path: Optional[str] = None,
|
|
77
|
+
cloud_workspace: bool = False,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""
|
|
80
|
+
A class for integrating Evidently for MLRun model monitoring within a monitoring application.
|
|
81
|
+
|
|
82
|
+
.. note::
|
|
83
|
+
|
|
84
|
+
The ``evidently`` package is not installed by default in the mlrun/mlrun image.
|
|
85
|
+
It must be installed separately to use this class.
|
|
86
|
+
|
|
87
|
+
:param evidently_project_id: (str) The ID of the Evidently project.
|
|
88
|
+
:param evidently_workspace_path: (str) The path to the Evidently workspace.
|
|
89
|
+
:param cloud_workspace: (bool) Whether the workspace is an Evidently Cloud workspace.
|
|
90
|
+
"""
|
|
91
|
+
if not _HAS_EVIDENTLY:
|
|
92
|
+
raise ModuleNotFoundError("Evidently is not installed - the app cannot run")
|
|
93
|
+
self.evidently_workspace_path = evidently_workspace_path
|
|
94
|
+
if cloud_workspace:
|
|
95
|
+
self.get_workspace = self.get_cloud_workspace
|
|
96
|
+
self.evidently_workspace = self.get_workspace()
|
|
97
|
+
self.evidently_project_id = evidently_project_id
|
|
98
|
+
self.evidently_project = self.load_project()
|
|
99
|
+
|
|
100
|
+
def load_project(self) -> "Project":
|
|
101
|
+
"""Load the Evidently project."""
|
|
102
|
+
return self.evidently_workspace.get_project(self.evidently_project_id)
|
|
103
|
+
|
|
104
|
+
def get_workspace(self) -> "WorkspaceBase":
|
|
105
|
+
"""Get the Evidently workspace. Override this method for customize access to the workspace."""
|
|
106
|
+
if self.evidently_workspace_path:
|
|
107
|
+
return Workspace.create(self.evidently_workspace_path)
|
|
108
|
+
else:
|
|
109
|
+
raise MLRunValueError(
|
|
110
|
+
"A local workspace could not be created as `evidently_workspace_path` is not set.\n"
|
|
111
|
+
"If you intend to use a cloud workspace, please use `cloud_workspace=True` and set the "
|
|
112
|
+
"`EVIDENTLY_API_KEY` environment variable. In other cases, override this method."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def get_cloud_workspace(self) -> "CloudWorkspace":
|
|
116
|
+
"""Load the Evidently cloud workspace according to the `EVIDENTLY_API_KEY` environment variable."""
|
|
117
|
+
return CloudWorkspace()
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def log_evidently_object(
|
|
121
|
+
monitoring_context: mm_context.MonitoringApplicationContext,
|
|
122
|
+
evidently_object: "Snapshot",
|
|
123
|
+
artifact_name: str,
|
|
124
|
+
unique_per_endpoint: bool = True,
|
|
125
|
+
) -> None:
|
|
126
|
+
"""
|
|
127
|
+
Logs an Evidently report or suite as an artifact.
|
|
128
|
+
|
|
129
|
+
.. caution::
|
|
130
|
+
|
|
131
|
+
Logging Evidently objects in every model monitoring window may cause scale issues.
|
|
132
|
+
This method should be called on special occasions only.
|
|
133
|
+
|
|
134
|
+
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
|
|
135
|
+
:param evidently_object: (Snapshot) The Evidently run to log, e.g. a report run.
|
|
136
|
+
:param artifact_name: (str) The name for the logged artifact.
|
|
137
|
+
:param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
|
|
138
|
+
set to ``False`` without changing item key will cause artifact override.
|
|
139
|
+
"""
|
|
140
|
+
with NamedTemporaryFile(suffix=".html") as file:
|
|
141
|
+
evidently_object.save_html(filename=file.name)
|
|
142
|
+
monitoring_context.log_artifact(
|
|
143
|
+
artifact_name,
|
|
144
|
+
local_path=file.name,
|
|
145
|
+
unique_per_endpoint=unique_per_endpoint,
|
|
146
|
+
)
|