mlrun 1.7.1rc4__py3-none-any.whl → 1.8.0rc8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__init__.py +23 -21
- mlrun/__main__.py +3 -3
- mlrun/alerts/alert.py +148 -14
- mlrun/artifacts/__init__.py +1 -2
- mlrun/artifacts/base.py +46 -12
- mlrun/artifacts/dataset.py +16 -16
- mlrun/artifacts/document.py +334 -0
- mlrun/artifacts/manager.py +15 -13
- mlrun/artifacts/model.py +66 -53
- mlrun/common/constants.py +7 -0
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/feature_set.py +1 -0
- mlrun/common/formatters/function.py +1 -0
- mlrun/{model_monitoring/db/stores/base/__init__.py → common/formatters/model_endpoint.py} +16 -1
- mlrun/common/formatters/pipeline.py +1 -2
- mlrun/common/formatters/project.py +9 -0
- mlrun/common/model_monitoring/__init__.py +0 -5
- mlrun/common/model_monitoring/helpers.py +1 -29
- mlrun/common/runtimes/constants.py +1 -2
- mlrun/common/schemas/__init__.py +6 -2
- mlrun/common/schemas/alert.py +111 -19
- mlrun/common/schemas/api_gateway.py +3 -3
- mlrun/common/schemas/artifact.py +11 -7
- mlrun/common/schemas/auth.py +6 -4
- mlrun/common/schemas/background_task.py +7 -7
- mlrun/common/schemas/client_spec.py +2 -3
- mlrun/common/schemas/clusterization_spec.py +2 -2
- mlrun/common/schemas/common.py +53 -3
- mlrun/common/schemas/constants.py +15 -0
- mlrun/common/schemas/datastore_profile.py +1 -1
- mlrun/common/schemas/feature_store.py +9 -9
- mlrun/common/schemas/frontend_spec.py +4 -4
- mlrun/common/schemas/function.py +10 -10
- mlrun/common/schemas/hub.py +1 -1
- mlrun/common/schemas/k8s.py +3 -3
- mlrun/common/schemas/memory_reports.py +3 -3
- mlrun/common/schemas/model_monitoring/__init__.py +2 -1
- mlrun/common/schemas/model_monitoring/constants.py +66 -14
- mlrun/common/schemas/model_monitoring/grafana.py +1 -1
- mlrun/common/schemas/model_monitoring/model_endpoints.py +91 -147
- mlrun/common/schemas/notification.py +24 -3
- mlrun/common/schemas/object.py +1 -1
- mlrun/common/schemas/pagination.py +4 -4
- mlrun/common/schemas/partition.py +137 -0
- mlrun/common/schemas/pipeline.py +2 -2
- mlrun/common/schemas/project.py +25 -17
- mlrun/common/schemas/runs.py +2 -2
- mlrun/common/schemas/runtime_resource.py +5 -5
- mlrun/common/schemas/schedule.py +1 -1
- mlrun/common/schemas/secret.py +1 -1
- mlrun/common/schemas/tag.py +3 -3
- mlrun/common/schemas/workflow.py +5 -5
- mlrun/config.py +67 -10
- mlrun/data_types/__init__.py +0 -2
- mlrun/data_types/infer.py +3 -1
- mlrun/data_types/spark.py +2 -1
- mlrun/datastore/__init__.py +0 -2
- mlrun/datastore/alibaba_oss.py +4 -1
- mlrun/datastore/azure_blob.py +4 -1
- mlrun/datastore/base.py +12 -4
- mlrun/datastore/datastore.py +9 -3
- mlrun/datastore/datastore_profile.py +79 -20
- mlrun/datastore/dbfs_store.py +4 -1
- mlrun/datastore/filestore.py +4 -1
- mlrun/datastore/google_cloud_storage.py +4 -1
- mlrun/datastore/hdfs.py +4 -1
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +4 -1
- mlrun/datastore/s3.py +4 -1
- mlrun/datastore/sources.py +52 -51
- mlrun/datastore/store_resources.py +0 -2
- mlrun/datastore/targets.py +21 -21
- mlrun/datastore/utils.py +2 -2
- mlrun/datastore/v3io.py +4 -1
- mlrun/datastore/vectorstore.py +194 -0
- mlrun/datastore/wasbfs/fs.py +13 -12
- mlrun/db/base.py +208 -82
- mlrun/db/factory.py +0 -3
- mlrun/db/httpdb.py +1237 -386
- mlrun/db/nopdb.py +201 -74
- mlrun/errors.py +2 -2
- mlrun/execution.py +136 -50
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +41 -40
- mlrun/feature_store/common.py +9 -9
- mlrun/feature_store/feature_set.py +20 -18
- mlrun/feature_store/feature_vector.py +27 -24
- mlrun/feature_store/retrieval/base.py +14 -9
- mlrun/feature_store/retrieval/job.py +2 -1
- mlrun/feature_store/steps.py +2 -2
- mlrun/features.py +30 -13
- mlrun/frameworks/__init__.py +1 -2
- mlrun/frameworks/_common/__init__.py +1 -2
- mlrun/frameworks/_common/artifacts_library.py +2 -2
- mlrun/frameworks/_common/mlrun_interface.py +10 -6
- mlrun/frameworks/_common/model_handler.py +29 -27
- mlrun/frameworks/_common/producer.py +3 -1
- mlrun/frameworks/_dl_common/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
- mlrun/frameworks/_ml_common/__init__.py +1 -2
- mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_ml_common/model_handler.py +21 -21
- mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/auto_mlrun/__init__.py +1 -2
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
- mlrun/frameworks/huggingface/__init__.py +1 -2
- mlrun/frameworks/huggingface/model_server.py +9 -9
- mlrun/frameworks/lgbm/__init__.py +47 -44
- mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
- mlrun/frameworks/lgbm/model_handler.py +15 -11
- mlrun/frameworks/lgbm/model_server.py +11 -7
- mlrun/frameworks/lgbm/utils.py +2 -2
- mlrun/frameworks/onnx/__init__.py +1 -2
- mlrun/frameworks/onnx/dataset.py +3 -3
- mlrun/frameworks/onnx/mlrun_interface.py +2 -2
- mlrun/frameworks/onnx/model_handler.py +7 -5
- mlrun/frameworks/onnx/model_server.py +8 -6
- mlrun/frameworks/parallel_coordinates.py +11 -11
- mlrun/frameworks/pytorch/__init__.py +22 -23
- mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
- mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
- mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
- mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
- mlrun/frameworks/pytorch/model_handler.py +21 -17
- mlrun/frameworks/pytorch/model_server.py +13 -9
- mlrun/frameworks/sklearn/__init__.py +19 -18
- mlrun/frameworks/sklearn/estimator.py +2 -2
- mlrun/frameworks/sklearn/metric.py +3 -3
- mlrun/frameworks/sklearn/metrics_library.py +8 -6
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
- mlrun/frameworks/sklearn/model_handler.py +4 -3
- mlrun/frameworks/tf_keras/__init__.py +11 -12
- mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
- mlrun/frameworks/tf_keras/model_handler.py +17 -13
- mlrun/frameworks/tf_keras/model_server.py +12 -8
- mlrun/frameworks/xgboost/__init__.py +19 -18
- mlrun/frameworks/xgboost/model_handler.py +13 -9
- mlrun/launcher/base.py +3 -4
- mlrun/launcher/local.py +1 -1
- mlrun/launcher/remote.py +1 -1
- mlrun/lists.py +4 -3
- mlrun/model.py +117 -46
- mlrun/model_monitoring/__init__.py +4 -4
- mlrun/model_monitoring/api.py +61 -59
- mlrun/model_monitoring/applications/_application_steps.py +17 -17
- mlrun/model_monitoring/applications/base.py +165 -6
- mlrun/model_monitoring/applications/context.py +88 -37
- mlrun/model_monitoring/applications/evidently_base.py +1 -2
- mlrun/model_monitoring/applications/histogram_data_drift.py +43 -21
- mlrun/model_monitoring/applications/results.py +55 -3
- mlrun/model_monitoring/controller.py +207 -239
- mlrun/model_monitoring/db/__init__.py +0 -2
- mlrun/model_monitoring/db/_schedules.py +156 -0
- mlrun/model_monitoring/db/_stats.py +189 -0
- mlrun/model_monitoring/db/tsdb/base.py +78 -25
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +90 -16
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +279 -59
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +78 -17
- mlrun/model_monitoring/helpers.py +152 -49
- mlrun/model_monitoring/stream_processing.py +99 -283
- mlrun/model_monitoring/tracking_policy.py +10 -3
- mlrun/model_monitoring/writer.py +48 -36
- mlrun/package/__init__.py +3 -6
- mlrun/package/context_handler.py +1 -1
- mlrun/package/packager.py +12 -9
- mlrun/package/packagers/__init__.py +0 -2
- mlrun/package/packagers/default_packager.py +14 -11
- mlrun/package/packagers/numpy_packagers.py +16 -7
- mlrun/package/packagers/pandas_packagers.py +18 -18
- mlrun/package/packagers/python_standard_library_packagers.py +25 -11
- mlrun/package/packagers_manager.py +31 -14
- mlrun/package/utils/__init__.py +0 -3
- mlrun/package/utils/_pickler.py +6 -6
- mlrun/platforms/__init__.py +47 -16
- mlrun/platforms/iguazio.py +4 -1
- mlrun/projects/operations.py +27 -27
- mlrun/projects/pipelines.py +75 -38
- mlrun/projects/project.py +865 -206
- mlrun/run.py +53 -10
- mlrun/runtimes/__init__.py +1 -3
- mlrun/runtimes/base.py +15 -11
- mlrun/runtimes/daskjob.py +9 -9
- mlrun/runtimes/generators.py +2 -1
- mlrun/runtimes/kubejob.py +4 -5
- mlrun/runtimes/mounts.py +572 -0
- mlrun/runtimes/mpijob/__init__.py +0 -2
- mlrun/runtimes/mpijob/abstract.py +7 -6
- mlrun/runtimes/nuclio/api_gateway.py +7 -7
- mlrun/runtimes/nuclio/application/application.py +11 -11
- mlrun/runtimes/nuclio/function.py +19 -17
- mlrun/runtimes/nuclio/serving.py +18 -11
- mlrun/runtimes/pod.py +154 -45
- mlrun/runtimes/remotesparkjob.py +3 -2
- mlrun/runtimes/sparkjob/__init__.py +0 -2
- mlrun/runtimes/sparkjob/spark3job.py +21 -11
- mlrun/runtimes/utils.py +6 -5
- mlrun/serving/merger.py +6 -4
- mlrun/serving/remote.py +18 -17
- mlrun/serving/routers.py +185 -172
- mlrun/serving/server.py +7 -1
- mlrun/serving/states.py +97 -78
- mlrun/serving/utils.py +13 -2
- mlrun/serving/v1_serving.py +3 -2
- mlrun/serving/v2_serving.py +74 -65
- mlrun/track/__init__.py +1 -1
- mlrun/track/tracker.py +2 -2
- mlrun/track/trackers/mlflow_tracker.py +6 -5
- mlrun/utils/async_http.py +1 -1
- mlrun/utils/clones.py +1 -1
- mlrun/utils/helpers.py +66 -18
- mlrun/utils/logger.py +106 -4
- mlrun/utils/notifications/notification/__init__.py +22 -19
- mlrun/utils/notifications/notification/base.py +33 -14
- mlrun/utils/notifications/notification/console.py +6 -6
- mlrun/utils/notifications/notification/git.py +11 -11
- mlrun/utils/notifications/notification/ipython.py +10 -9
- mlrun/utils/notifications/notification/mail.py +176 -0
- mlrun/utils/notifications/notification/slack.py +6 -6
- mlrun/utils/notifications/notification/webhook.py +6 -6
- mlrun/utils/notifications/notification_pusher.py +86 -44
- mlrun/utils/regex.py +3 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.1rc4.dist-info → mlrun-1.8.0rc8.dist-info}/METADATA +191 -186
- mlrun-1.8.0rc8.dist-info/RECORD +347 -0
- {mlrun-1.7.1rc4.dist-info → mlrun-1.8.0rc8.dist-info}/WHEEL +1 -1
- mlrun/model_monitoring/db/stores/__init__.py +0 -136
- mlrun/model_monitoring/db/stores/base/store.py +0 -213
- mlrun/model_monitoring/db/stores/sqldb/__init__.py +0 -13
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -71
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -190
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -103
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -40
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -659
- mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +0 -13
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -726
- mlrun/model_monitoring/model_endpoint.py +0 -118
- mlrun-1.7.1rc4.dist-info/RECORD +0 -351
- {mlrun-1.7.1rc4.dist-info → mlrun-1.8.0rc8.dist-info}/LICENSE +0 -0
- {mlrun-1.7.1rc4.dist-info → mlrun-1.8.0rc8.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.1rc4.dist-info → mlrun-1.8.0rc8.dist-info}/top_level.txt +0 -0
|
@@ -16,6 +16,7 @@ import json
|
|
|
16
16
|
import traceback
|
|
17
17
|
from typing import Any, Optional, Union
|
|
18
18
|
|
|
19
|
+
import mlrun.common.schemas
|
|
19
20
|
import mlrun.common.schemas.alert as alert_objects
|
|
20
21
|
import mlrun.common.schemas.model_monitoring.constants as mm_constant
|
|
21
22
|
import mlrun.datastore
|
|
@@ -26,7 +27,11 @@ from mlrun.serving.utils import StepToDict
|
|
|
26
27
|
from mlrun.utils import logger
|
|
27
28
|
|
|
28
29
|
from .context import MonitoringApplicationContext
|
|
29
|
-
from .results import
|
|
30
|
+
from .results import (
|
|
31
|
+
ModelMonitoringApplicationMetric,
|
|
32
|
+
ModelMonitoringApplicationResult,
|
|
33
|
+
_ModelMonitoringApplicationStats,
|
|
34
|
+
)
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
class _PushToMonitoringWriter(StepToDict):
|
|
@@ -61,7 +66,9 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
61
66
|
event: tuple[
|
|
62
67
|
list[
|
|
63
68
|
Union[
|
|
64
|
-
ModelMonitoringApplicationResult,
|
|
69
|
+
ModelMonitoringApplicationResult,
|
|
70
|
+
ModelMonitoringApplicationMetric,
|
|
71
|
+
_ModelMonitoringApplicationStats,
|
|
65
72
|
]
|
|
66
73
|
],
|
|
67
74
|
MonitoringApplicationContext,
|
|
@@ -75,6 +82,7 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
75
82
|
self._lazy_init()
|
|
76
83
|
application_results, application_context = event
|
|
77
84
|
writer_event = {
|
|
85
|
+
mm_constant.WriterEvent.ENDPOINT_NAME: application_context.endpoint_name,
|
|
78
86
|
mm_constant.WriterEvent.APPLICATION_NAME: application_context.application_name,
|
|
79
87
|
mm_constant.WriterEvent.ENDPOINT_ID: application_context.endpoint_id,
|
|
80
88
|
mm_constant.WriterEvent.START_INFER_TIME: application_context.start_infer_time.isoformat(
|
|
@@ -90,21 +98,15 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
90
98
|
writer_event[mm_constant.WriterEvent.EVENT_KIND] = (
|
|
91
99
|
mm_constant.WriterEventKind.RESULT
|
|
92
100
|
)
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
elif isinstance(result, _ModelMonitoringApplicationStats):
|
|
102
|
+
writer_event[mm_constant.WriterEvent.EVENT_KIND] = (
|
|
103
|
+
mm_constant.WriterEventKind.STATS
|
|
95
104
|
)
|
|
96
|
-
writer_event[mm_constant.WriterEvent.DATA] = json.dumps(data)
|
|
97
105
|
else:
|
|
98
106
|
writer_event[mm_constant.WriterEvent.EVENT_KIND] = (
|
|
99
107
|
mm_constant.WriterEventKind.METRIC
|
|
100
108
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
writer_event[mm_constant.WriterEvent.EVENT_KIND] = (
|
|
104
|
-
mm_constant.WriterEventKind.RESULT
|
|
105
|
-
if isinstance(result, ModelMonitoringApplicationResult)
|
|
106
|
-
else mm_constant.WriterEventKind.METRIC
|
|
107
|
-
)
|
|
109
|
+
writer_event[mm_constant.WriterEvent.DATA] = json.dumps(data)
|
|
108
110
|
logger.info(
|
|
109
111
|
f"Pushing data = {writer_event} \n to stream = {self.stream_uri}"
|
|
110
112
|
)
|
|
@@ -113,9 +115,7 @@ class _PushToMonitoringWriter(StepToDict):
|
|
|
113
115
|
|
|
114
116
|
def _lazy_init(self):
|
|
115
117
|
if self.output_stream is None:
|
|
116
|
-
self.output_stream = mlrun.datastore.get_stream_pusher(
|
|
117
|
-
self.stream_uri,
|
|
118
|
-
)
|
|
118
|
+
self.output_stream = mlrun.datastore.get_stream_pusher(self.stream_uri)
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
class _PrepareMonitoringEvent(StepToDict):
|
|
@@ -127,7 +127,7 @@ class _PrepareMonitoringEvent(StepToDict):
|
|
|
127
127
|
"""
|
|
128
128
|
self.graph_context = context
|
|
129
129
|
self.application_name = application_name
|
|
130
|
-
self.model_endpoints: dict[str, mlrun.
|
|
130
|
+
self.model_endpoints: dict[str, mlrun.common.schemas.ModelEndpoint] = {}
|
|
131
131
|
|
|
132
132
|
def do(self, event: dict[str, Any]) -> MonitoringApplicationContext:
|
|
133
133
|
"""
|
|
@@ -137,10 +137,10 @@ class _PrepareMonitoringEvent(StepToDict):
|
|
|
137
137
|
:return: Application context.
|
|
138
138
|
"""
|
|
139
139
|
application_context = MonitoringApplicationContext(
|
|
140
|
-
graph_context=self.graph_context,
|
|
141
140
|
application_name=self.application_name,
|
|
142
141
|
event=event,
|
|
143
142
|
model_endpoint_dict=self.model_endpoints,
|
|
143
|
+
graph_context=self.graph_context,
|
|
144
144
|
)
|
|
145
145
|
|
|
146
146
|
self.model_endpoints.setdefault(
|
|
@@ -12,9 +12,18 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import socket
|
|
15
16
|
from abc import ABC, abstractmethod
|
|
16
|
-
from
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from typing import Any, Optional, Union, cast
|
|
17
19
|
|
|
20
|
+
import pandas as pd
|
|
21
|
+
|
|
22
|
+
import mlrun
|
|
23
|
+
import mlrun.common.constants as mlrun_constants
|
|
24
|
+
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
25
|
+
import mlrun.errors
|
|
26
|
+
import mlrun.model_monitoring.api as mm_api
|
|
18
27
|
import mlrun.model_monitoring.applications.context as mm_context
|
|
19
28
|
import mlrun.model_monitoring.applications.results as mm_results
|
|
20
29
|
from mlrun.serving.utils import MonitoringApplicationToDict
|
|
@@ -22,12 +31,12 @@ from mlrun.serving.utils import MonitoringApplicationToDict
|
|
|
22
31
|
|
|
23
32
|
class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
24
33
|
"""
|
|
25
|
-
|
|
34
|
+
The base class for a model monitoring application.
|
|
26
35
|
Inherit from this class to create a custom model monitoring application.
|
|
27
36
|
|
|
28
|
-
example
|
|
37
|
+
For example, :code:`MyApp` below is a simplistic custom application::
|
|
29
38
|
|
|
30
|
-
class MyApp(
|
|
39
|
+
class MyApp(ModelMonitoringApplicationBase):
|
|
31
40
|
def do_tracking(
|
|
32
41
|
self,
|
|
33
42
|
monitoring_context: mm_context.MonitoringApplicationContext,
|
|
@@ -43,8 +52,6 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
43
52
|
kind=mm_constant.ResultKindApp.data_drift,
|
|
44
53
|
status=mm_constant.ResultStatusApp.detected,
|
|
45
54
|
)
|
|
46
|
-
|
|
47
|
-
|
|
48
55
|
"""
|
|
49
56
|
|
|
50
57
|
kind = "monitoring_application"
|
|
@@ -62,6 +69,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
62
69
|
]:
|
|
63
70
|
"""
|
|
64
71
|
Process the monitoring event and return application results & metrics.
|
|
72
|
+
Note: this method is internal and should not be called directly or overridden.
|
|
65
73
|
|
|
66
74
|
:param monitoring_context: (MonitoringApplicationContext) The monitoring application context.
|
|
67
75
|
:returns: A tuple of:
|
|
@@ -80,6 +88,157 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
80
88
|
results = results if isinstance(results, list) else [results]
|
|
81
89
|
return results, monitoring_context
|
|
82
90
|
|
|
91
|
+
def _handler(
|
|
92
|
+
self,
|
|
93
|
+
context: "mlrun.MLClientCtx",
|
|
94
|
+
sample_data: Optional[pd.DataFrame] = None,
|
|
95
|
+
reference_data: Optional[pd.DataFrame] = None,
|
|
96
|
+
endpoint_names: Optional[list[str]] = None,
|
|
97
|
+
start: Optional[datetime] = None,
|
|
98
|
+
end: Optional[datetime] = None,
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
A custom handler that wraps the application's logic implemented in
|
|
102
|
+
:py:meth:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase.do_tracking`
|
|
103
|
+
for an MLRun job.
|
|
104
|
+
This method should not be called directly.
|
|
105
|
+
"""
|
|
106
|
+
feature_stats = (
|
|
107
|
+
mm_api.get_sample_set_statistics(reference_data)
|
|
108
|
+
if reference_data is not None
|
|
109
|
+
else None
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def call_do_tracking(event: Optional[dict] = None):
|
|
113
|
+
if event is None:
|
|
114
|
+
event = {}
|
|
115
|
+
monitoring_context = mm_context.MonitoringApplicationContext(
|
|
116
|
+
event=event,
|
|
117
|
+
application_name=self.__class__.__name__,
|
|
118
|
+
logger=context.logger,
|
|
119
|
+
artifacts_logger=context,
|
|
120
|
+
sample_df=sample_data,
|
|
121
|
+
feature_stats=feature_stats,
|
|
122
|
+
)
|
|
123
|
+
return self.do_tracking(monitoring_context)
|
|
124
|
+
|
|
125
|
+
if endpoint_names is not None:
|
|
126
|
+
start, end = self._validate_times(start, end)
|
|
127
|
+
for endpoint_name in endpoint_names:
|
|
128
|
+
result = call_do_tracking(
|
|
129
|
+
event={
|
|
130
|
+
mm_constants.ApplicationEvent.ENDPOINT_NAME: endpoint_name,
|
|
131
|
+
mm_constants.ApplicationEvent.START_INFER_TIME: start,
|
|
132
|
+
mm_constants.ApplicationEvent.END_INFER_TIME: end,
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
context.log_result(
|
|
136
|
+
f"{endpoint_name}_{start.isoformat()}_{end.isoformat()}", result
|
|
137
|
+
)
|
|
138
|
+
else:
|
|
139
|
+
return call_do_tracking()
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def _validate_times(
|
|
143
|
+
start: Optional[datetime], end: Optional[datetime]
|
|
144
|
+
) -> tuple[datetime, datetime]:
|
|
145
|
+
if (start is None) or (end is None):
|
|
146
|
+
raise mlrun.errors.MLRunValueError(
|
|
147
|
+
"When `endpoint_names` is provided, you must also pass the start and end times"
|
|
148
|
+
)
|
|
149
|
+
return start, end
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def evaluate(
|
|
153
|
+
cls,
|
|
154
|
+
func_path: Optional[str] = None,
|
|
155
|
+
func_name: Optional[str] = None,
|
|
156
|
+
*,
|
|
157
|
+
tag: Optional[str] = None,
|
|
158
|
+
run_local: bool = True,
|
|
159
|
+
sample_data: Optional[pd.DataFrame] = None,
|
|
160
|
+
reference_data: Optional[pd.DataFrame] = None,
|
|
161
|
+
image: Optional[str] = None,
|
|
162
|
+
with_repo: Optional[bool] = False,
|
|
163
|
+
requirements: Optional[Union[str, list[str]]] = None,
|
|
164
|
+
requirements_file: str = "",
|
|
165
|
+
endpoint_names: Optional[list[str]] = None,
|
|
166
|
+
start: Optional[datetime] = None,
|
|
167
|
+
end: Optional[datetime] = None,
|
|
168
|
+
) -> "mlrun.RunObject":
|
|
169
|
+
"""
|
|
170
|
+
Call this function to run the application's
|
|
171
|
+
:py:meth:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase.do_tracking`
|
|
172
|
+
model monitoring logic as a :py:class:`~mlrun.runtimes.KubejobRuntime`, which is an MLRun function.
|
|
173
|
+
|
|
174
|
+
:param func_path: The path to the function. If not passed, the current notebook is used.
|
|
175
|
+
:param func_name: The name of the function. If not passed, the class name is used.
|
|
176
|
+
:param tag: An optional tag for the function.
|
|
177
|
+
:param run_local: Whether to run the function locally or remotely.
|
|
178
|
+
:param sample_df: Optional - pandas data-frame as the current dataset.
|
|
179
|
+
When set, it replaces the data read from the model endpoint's offline source.
|
|
180
|
+
:param feature_stats: Optional - statistics dictionary of the reference data.
|
|
181
|
+
When set, it overrides the model endpoint's feature stats.
|
|
182
|
+
:param image: Docker image to run the job on.
|
|
183
|
+
:param with_repo: Whether to clone the current repo to the build source.
|
|
184
|
+
:param requirements: List of Python requirements to be installed in the image.
|
|
185
|
+
:param requirements_file: Path to a Python requirements file to be installed in the image.
|
|
186
|
+
:param endpoint_names: The model endpoint names to get the data from. When the names are passed,
|
|
187
|
+
you have to provide also the start and end times of the data to analyze.
|
|
188
|
+
:param start: The start time of the sample data.
|
|
189
|
+
:param end: The end time of the sample data.
|
|
190
|
+
|
|
191
|
+
:returns: The output of the
|
|
192
|
+
:py:meth:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase.do_tracking`
|
|
193
|
+
method with the given parameters and inputs, wrapped in a :py:class:`~mlrun.model.RunObject`.
|
|
194
|
+
"""
|
|
195
|
+
project = cast("mlrun.MlrunProject", mlrun.get_current_project())
|
|
196
|
+
class_name = cls.__name__
|
|
197
|
+
job_name = func_name if func_name is not None else class_name
|
|
198
|
+
handler = f"{class_name}::{cls._handler.__name__}"
|
|
199
|
+
|
|
200
|
+
job = cast(
|
|
201
|
+
mlrun.runtimes.KubejobRuntime,
|
|
202
|
+
project.set_function(
|
|
203
|
+
func=func_path,
|
|
204
|
+
name=job_name,
|
|
205
|
+
kind=mlrun.runtimes.KubejobRuntime.kind,
|
|
206
|
+
handler=handler,
|
|
207
|
+
tag=tag,
|
|
208
|
+
image=image,
|
|
209
|
+
with_repo=with_repo,
|
|
210
|
+
requirements=requirements,
|
|
211
|
+
requirements_file=requirements_file,
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
params: dict[str, Union[list[str], datetime]] = {}
|
|
216
|
+
if endpoint_names:
|
|
217
|
+
start, end = cls._validate_times(start, end)
|
|
218
|
+
params["endpoint_names"] = endpoint_names
|
|
219
|
+
params["start"] = start
|
|
220
|
+
params["end"] = end
|
|
221
|
+
|
|
222
|
+
inputs: dict[str, str] = {}
|
|
223
|
+
for data, identifier in [
|
|
224
|
+
(sample_data, "sample_data"),
|
|
225
|
+
(reference_data, "reference_data"),
|
|
226
|
+
]:
|
|
227
|
+
if data is not None:
|
|
228
|
+
key = f"{job_name}_{identifier}"
|
|
229
|
+
inputs[identifier] = project.log_dataset(
|
|
230
|
+
key,
|
|
231
|
+
data,
|
|
232
|
+
labels={
|
|
233
|
+
mlrun_constants.MLRunInternalLabels.runner_pod: socket.gethostname(),
|
|
234
|
+
mlrun_constants.MLRunInternalLabels.producer_type: "model-monitoring-job",
|
|
235
|
+
mlrun_constants.MLRunInternalLabels.app_name: class_name,
|
|
236
|
+
},
|
|
237
|
+
).uri
|
|
238
|
+
|
|
239
|
+
run_result = job.run(local=run_local, params=params, inputs=inputs)
|
|
240
|
+
return run_result
|
|
241
|
+
|
|
83
242
|
@abstractmethod
|
|
84
243
|
def do_tracking(
|
|
85
244
|
self,
|
|
@@ -12,26 +12,36 @@
|
|
|
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
|
|
25
26
|
import mlrun.features
|
|
26
27
|
import mlrun.serving
|
|
27
28
|
import mlrun.utils
|
|
28
29
|
from mlrun.artifacts import Artifact, DatasetArtifact, ModelArtifact, get_model
|
|
29
|
-
from mlrun.common.model_monitoring.helpers import FeatureStats
|
|
30
|
+
from mlrun.common.model_monitoring.helpers import FeatureStats
|
|
31
|
+
from mlrun.common.schemas import ModelEndpoint
|
|
30
32
|
from mlrun.model_monitoring.helpers import (
|
|
31
33
|
calculate_inputs_statistics,
|
|
32
|
-
get_endpoint_record,
|
|
33
34
|
)
|
|
34
|
-
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _ArtifactsLogger(Protocol):
|
|
38
|
+
"""
|
|
39
|
+
Classes that implement this protocol are :code:`MlrunProject` and :code:`MLClientCtx`.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def log_artifact(self, *args, **kwargs) -> Artifact: ...
|
|
43
|
+
def log_dataset(self, *args, **kwargs) -> DatasetArtifact: ...
|
|
44
|
+
def log_model(self, *args, **kwargs) -> ModelArtifact: ...
|
|
35
45
|
|
|
36
46
|
|
|
37
47
|
class MonitoringApplicationContext:
|
|
@@ -52,6 +62,7 @@ class MonitoringApplicationContext:
|
|
|
52
62
|
:param end_infer_time: (pd.Timestamp) End time of the monitoring schedule.
|
|
53
63
|
:param latest_request: (pd.Timestamp) Timestamp of the latest request on this endpoint_id.
|
|
54
64
|
:param endpoint_id: (str) ID of the monitored model endpoint
|
|
65
|
+
:param endpoint_name: (str) Name of the monitored model endpoint
|
|
55
66
|
:param output_stream_uri: (str) URI of the output stream for results
|
|
56
67
|
:param model_endpoint: (ModelEndpoint) The model endpoint object.
|
|
57
68
|
:param feature_names: (list[str]) List of models feature names.
|
|
@@ -60,36 +71,71 @@ class MonitoringApplicationContext:
|
|
|
60
71
|
and a list of extra data items.
|
|
61
72
|
"""
|
|
62
73
|
|
|
74
|
+
_logger_name = "monitoring-application"
|
|
75
|
+
|
|
63
76
|
def __init__(
|
|
64
77
|
self,
|
|
65
78
|
*,
|
|
66
|
-
graph_context: mlrun.serving.GraphContext,
|
|
67
79
|
application_name: str,
|
|
68
80
|
event: dict[str, Any],
|
|
69
|
-
model_endpoint_dict: dict[str, ModelEndpoint],
|
|
81
|
+
model_endpoint_dict: Optional[dict[str, ModelEndpoint]] = None,
|
|
82
|
+
logger: Optional[mlrun.utils.Logger] = None,
|
|
83
|
+
graph_context: Optional[mlrun.serving.GraphContext] = None,
|
|
84
|
+
context: Optional["mlrun.MLClientCtx"] = None,
|
|
85
|
+
artifacts_logger: Optional[_ArtifactsLogger] = None,
|
|
86
|
+
sample_df: Optional[pd.DataFrame] = None,
|
|
87
|
+
feature_stats: Optional[FeatureStats] = None,
|
|
70
88
|
) -> None:
|
|
71
89
|
"""
|
|
72
|
-
|
|
90
|
+
The :code:`__init__` method initializes a :code:`MonitoringApplicationContext` object
|
|
91
|
+
and has the following attributes.
|
|
73
92
|
Note: this object should not be instantiated manually.
|
|
74
93
|
|
|
75
94
|
:param application_name: The application name.
|
|
76
95
|
:param event: The instance data dictionary.
|
|
77
|
-
:param model_endpoint_dict:
|
|
96
|
+
:param model_endpoint_dict: Optional - dictionary of model endpoints.
|
|
97
|
+
:param logger: Optional - MLRun logger instance.
|
|
98
|
+
:param graph_context: Optional - GraphContext instance.
|
|
99
|
+
:param context: Optional - MLClientCtx instance.
|
|
100
|
+
:param artifacts_logger: Optional - an object that can log artifacts,
|
|
101
|
+
typically :py:class:`~mlrun.projects.MlrunProject` or
|
|
102
|
+
:py:class:`~mlrun.execution.MLClientCtx`.
|
|
103
|
+
:param sample_df: Optional - pandas data-frame as the current dataset.
|
|
104
|
+
When set, it replaces the data read from the offline source.
|
|
105
|
+
:param feature_stats: Optional - statistics dictionary of the reference data.
|
|
106
|
+
When set, it overrides the model endpoint's feature stats.
|
|
78
107
|
"""
|
|
79
108
|
self.application_name = application_name
|
|
80
109
|
|
|
81
|
-
|
|
82
|
-
|
|
110
|
+
if graph_context:
|
|
111
|
+
self.project_name = graph_context.project
|
|
112
|
+
self.project = mlrun.load_project(url=self.project_name)
|
|
113
|
+
elif context:
|
|
114
|
+
potential_project = context.get_project_object()
|
|
115
|
+
if not potential_project:
|
|
116
|
+
raise mlrun.errors.MLRunValueError(
|
|
117
|
+
"Could not load project from context"
|
|
118
|
+
)
|
|
119
|
+
self.project = potential_project
|
|
120
|
+
self.project_name = self.project.name
|
|
121
|
+
|
|
122
|
+
self._artifacts_logger: _ArtifactsLogger = artifacts_logger or self.project
|
|
83
123
|
|
|
84
124
|
# MLRun Logger
|
|
85
|
-
self.logger = mlrun.utils.create_logger(
|
|
125
|
+
self.logger = logger or mlrun.utils.create_logger(
|
|
86
126
|
level=mlrun.mlconf.log_level,
|
|
87
127
|
formatter_kind=mlrun.mlconf.log_formatter,
|
|
88
|
-
name=
|
|
128
|
+
name=self._logger_name,
|
|
89
129
|
)
|
|
90
130
|
# Nuclio logger - `nuclio.request.Logger`.
|
|
91
|
-
# Note: this logger
|
|
92
|
-
self.nuclio_logger =
|
|
131
|
+
# Note: this logger accepts keyword arguments only in its `_with` methods, e.g. `info_with`.
|
|
132
|
+
self.nuclio_logger = (
|
|
133
|
+
graph_context.logger
|
|
134
|
+
if graph_context
|
|
135
|
+
else nuclio.request.Logger(
|
|
136
|
+
level=mlrun.mlconf.log_level, name=self._logger_name
|
|
137
|
+
)
|
|
138
|
+
)
|
|
93
139
|
|
|
94
140
|
# event data
|
|
95
141
|
self.start_infer_time = pd.Timestamp(
|
|
@@ -101,29 +147,38 @@ class MonitoringApplicationContext:
|
|
|
101
147
|
self.endpoint_id = cast(
|
|
102
148
|
str, event.get(mm_constants.ApplicationEvent.ENDPOINT_ID)
|
|
103
149
|
)
|
|
150
|
+
self.endpoint_name = cast(
|
|
151
|
+
str, event.get(mm_constants.ApplicationEvent.ENDPOINT_NAME)
|
|
152
|
+
)
|
|
104
153
|
self.output_stream_uri = cast(
|
|
105
154
|
str, event.get(mm_constants.ApplicationEvent.OUTPUT_STREAM_URI)
|
|
106
155
|
)
|
|
107
156
|
|
|
108
|
-
self._feature_stats: Optional[FeatureStats] =
|
|
157
|
+
self._feature_stats: Optional[FeatureStats] = feature_stats
|
|
109
158
|
self._sample_df_stats: Optional[FeatureStats] = None
|
|
110
159
|
|
|
111
160
|
# Default labels for the artifacts
|
|
112
161
|
self._default_labels = self._get_default_labels()
|
|
113
162
|
|
|
114
163
|
# Persistent data - fetched when needed
|
|
115
|
-
self._sample_df: Optional[pd.DataFrame] =
|
|
116
|
-
self._model_endpoint: Optional[ModelEndpoint] =
|
|
117
|
-
self.endpoint_id
|
|
164
|
+
self._sample_df: Optional[pd.DataFrame] = sample_df
|
|
165
|
+
self._model_endpoint: Optional[ModelEndpoint] = (
|
|
166
|
+
model_endpoint_dict.get(self.endpoint_id) if model_endpoint_dict else None
|
|
118
167
|
)
|
|
119
168
|
|
|
120
169
|
def _get_default_labels(self) -> dict[str, str]:
|
|
121
|
-
|
|
170
|
+
labels = {
|
|
122
171
|
mlrun_constants.MLRunInternalLabels.runner_pod: socket.gethostname(),
|
|
123
172
|
mlrun_constants.MLRunInternalLabels.producer_type: "model-monitoring-app",
|
|
124
173
|
mlrun_constants.MLRunInternalLabels.app_name: self.application_name,
|
|
125
|
-
mlrun_constants.MLRunInternalLabels.endpoint_id: self.endpoint_id,
|
|
126
174
|
}
|
|
175
|
+
for key, value in [
|
|
176
|
+
(mlrun_constants.MLRunInternalLabels.endpoint_id, self.endpoint_id),
|
|
177
|
+
(mlrun_constants.MLRunInternalLabels.endpoint_name, self.endpoint_name),
|
|
178
|
+
]:
|
|
179
|
+
if value:
|
|
180
|
+
labels[key] = value
|
|
181
|
+
return labels
|
|
127
182
|
|
|
128
183
|
def _add_default_labels(self, labels: Optional[dict[str, str]]) -> dict[str, str]:
|
|
129
184
|
"""Add the default labels to logged artifacts labels"""
|
|
@@ -133,7 +188,7 @@ class MonitoringApplicationContext:
|
|
|
133
188
|
def sample_df(self) -> pd.DataFrame:
|
|
134
189
|
if self._sample_df is None:
|
|
135
190
|
feature_set = fstore.get_feature_set(
|
|
136
|
-
self.model_endpoint.
|
|
191
|
+
self.model_endpoint.spec.monitoring_feature_set_uri
|
|
137
192
|
)
|
|
138
193
|
features = [f"{feature_set.metadata.name}.*"]
|
|
139
194
|
vector = fstore.FeatureVector(
|
|
@@ -155,16 +210,18 @@ class MonitoringApplicationContext:
|
|
|
155
210
|
@property
|
|
156
211
|
def model_endpoint(self) -> ModelEndpoint:
|
|
157
212
|
if not self._model_endpoint:
|
|
158
|
-
self._model_endpoint =
|
|
159
|
-
|
|
213
|
+
self._model_endpoint = mlrun.db.get_run_db().get_model_endpoint(
|
|
214
|
+
name=self.endpoint_name,
|
|
215
|
+
project=self.project_name,
|
|
216
|
+
endpoint_id=self.endpoint_id,
|
|
217
|
+
feature_analysis=True,
|
|
160
218
|
)
|
|
161
219
|
return self._model_endpoint
|
|
162
220
|
|
|
163
221
|
@property
|
|
164
222
|
def feature_stats(self) -> FeatureStats:
|
|
165
223
|
if not self._feature_stats:
|
|
166
|
-
self._feature_stats =
|
|
167
|
-
pad_features_hist(self._feature_stats)
|
|
224
|
+
self._feature_stats = self.model_endpoint.spec.feature_stats
|
|
168
225
|
return self._feature_stats
|
|
169
226
|
|
|
170
227
|
@property
|
|
@@ -179,18 +236,12 @@ class MonitoringApplicationContext:
|
|
|
179
236
|
@property
|
|
180
237
|
def feature_names(self) -> list[str]:
|
|
181
238
|
"""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
|
-
)
|
|
239
|
+
return self.model_endpoint.spec.feature_names
|
|
188
240
|
|
|
189
241
|
@property
|
|
190
242
|
def label_names(self) -> list[str]:
|
|
191
243
|
"""The label names of the model"""
|
|
192
|
-
|
|
193
|
-
return label_names if isinstance(label_names, list) else json.loads(label_names)
|
|
244
|
+
return self.model_endpoint.spec.label_names
|
|
194
245
|
|
|
195
246
|
@property
|
|
196
247
|
def model(self) -> tuple[str, ModelArtifact, dict]:
|
|
@@ -237,7 +288,7 @@ class MonitoringApplicationContext:
|
|
|
237
288
|
See :func:`~mlrun.projects.MlrunProject.log_artifact` for the documentation.
|
|
238
289
|
"""
|
|
239
290
|
labels = self._add_default_labels(labels)
|
|
240
|
-
return self.
|
|
291
|
+
return self._artifacts_logger.log_artifact(
|
|
241
292
|
item,
|
|
242
293
|
body=body,
|
|
243
294
|
tag=tag,
|
|
@@ -272,7 +323,7 @@ class MonitoringApplicationContext:
|
|
|
272
323
|
See :func:`~mlrun.projects.MlrunProject.log_dataset` for the documentation.
|
|
273
324
|
"""
|
|
274
325
|
labels = self._add_default_labels(labels)
|
|
275
|
-
return self.
|
|
326
|
+
return self._artifacts_logger.log_dataset(
|
|
276
327
|
key,
|
|
277
328
|
df,
|
|
278
329
|
tag=tag,
|
|
@@ -317,7 +368,7 @@ class MonitoringApplicationContext:
|
|
|
317
368
|
See :func:`~mlrun.projects.MlrunProject.log_model` for the documentation.
|
|
318
369
|
"""
|
|
319
370
|
labels = self._add_default_labels(labels)
|
|
320
|
-
return self.
|
|
371
|
+
return self._artifacts_logger.log_model(
|
|
321
372
|
key,
|
|
322
373
|
body=body,
|
|
323
374
|
framework=framework,
|
|
@@ -23,7 +23,7 @@ import mlrun.model_monitoring.applications.base as mm_base
|
|
|
23
23
|
import mlrun.model_monitoring.applications.context as mm_context
|
|
24
24
|
from mlrun.errors import MLRunIncompatibleVersionError
|
|
25
25
|
|
|
26
|
-
SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.4.
|
|
26
|
+
SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.4.39")
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def _check_evidently_version(*, cur: semver.Version, ref: semver.Version) -> None:
|
|
@@ -76,7 +76,6 @@ class EvidentlyModelMonitoringApplicationBase(
|
|
|
76
76
|
|
|
77
77
|
:param evidently_workspace_path: (str) The path to the Evidently workspace.
|
|
78
78
|
:param evidently_project_id: (str) The ID of the Evidently project.
|
|
79
|
-
|
|
80
79
|
"""
|
|
81
80
|
|
|
82
81
|
# TODO : more then one project (mep -> project)
|