mlrun 1.7.1rc10__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.

Files changed (257) hide show
  1. mlrun/__init__.py +23 -21
  2. mlrun/__main__.py +3 -3
  3. mlrun/alerts/alert.py +148 -14
  4. mlrun/artifacts/__init__.py +1 -2
  5. mlrun/artifacts/base.py +46 -12
  6. mlrun/artifacts/dataset.py +16 -16
  7. mlrun/artifacts/document.py +334 -0
  8. mlrun/artifacts/manager.py +15 -13
  9. mlrun/artifacts/model.py +66 -53
  10. mlrun/common/constants.py +7 -0
  11. mlrun/common/formatters/__init__.py +1 -0
  12. mlrun/common/formatters/feature_set.py +1 -0
  13. mlrun/common/formatters/function.py +1 -0
  14. mlrun/{model_monitoring/db/stores/base/__init__.py → common/formatters/model_endpoint.py} +16 -1
  15. mlrun/common/formatters/pipeline.py +1 -2
  16. mlrun/common/formatters/project.py +9 -0
  17. mlrun/common/model_monitoring/__init__.py +0 -5
  18. mlrun/common/model_monitoring/helpers.py +1 -29
  19. mlrun/common/runtimes/constants.py +1 -2
  20. mlrun/common/schemas/__init__.py +6 -2
  21. mlrun/common/schemas/alert.py +111 -19
  22. mlrun/common/schemas/api_gateway.py +3 -3
  23. mlrun/common/schemas/artifact.py +11 -7
  24. mlrun/common/schemas/auth.py +6 -4
  25. mlrun/common/schemas/background_task.py +7 -7
  26. mlrun/common/schemas/client_spec.py +2 -3
  27. mlrun/common/schemas/clusterization_spec.py +2 -2
  28. mlrun/common/schemas/common.py +53 -3
  29. mlrun/common/schemas/constants.py +15 -0
  30. mlrun/common/schemas/datastore_profile.py +1 -1
  31. mlrun/common/schemas/feature_store.py +9 -9
  32. mlrun/common/schemas/frontend_spec.py +4 -4
  33. mlrun/common/schemas/function.py +10 -10
  34. mlrun/common/schemas/hub.py +1 -1
  35. mlrun/common/schemas/k8s.py +3 -3
  36. mlrun/common/schemas/memory_reports.py +3 -3
  37. mlrun/common/schemas/model_monitoring/__init__.py +2 -1
  38. mlrun/common/schemas/model_monitoring/constants.py +66 -14
  39. mlrun/common/schemas/model_monitoring/grafana.py +1 -1
  40. mlrun/common/schemas/model_monitoring/model_endpoints.py +91 -147
  41. mlrun/common/schemas/notification.py +24 -3
  42. mlrun/common/schemas/object.py +1 -1
  43. mlrun/common/schemas/pagination.py +4 -4
  44. mlrun/common/schemas/partition.py +137 -0
  45. mlrun/common/schemas/pipeline.py +2 -2
  46. mlrun/common/schemas/project.py +25 -17
  47. mlrun/common/schemas/runs.py +2 -2
  48. mlrun/common/schemas/runtime_resource.py +5 -5
  49. mlrun/common/schemas/schedule.py +1 -1
  50. mlrun/common/schemas/secret.py +1 -1
  51. mlrun/common/schemas/tag.py +3 -3
  52. mlrun/common/schemas/workflow.py +5 -5
  53. mlrun/config.py +67 -10
  54. mlrun/data_types/__init__.py +0 -2
  55. mlrun/data_types/infer.py +3 -1
  56. mlrun/data_types/spark.py +2 -1
  57. mlrun/datastore/__init__.py +0 -2
  58. mlrun/datastore/alibaba_oss.py +4 -1
  59. mlrun/datastore/azure_blob.py +4 -1
  60. mlrun/datastore/base.py +12 -4
  61. mlrun/datastore/datastore.py +9 -3
  62. mlrun/datastore/datastore_profile.py +79 -20
  63. mlrun/datastore/dbfs_store.py +4 -1
  64. mlrun/datastore/filestore.py +4 -1
  65. mlrun/datastore/google_cloud_storage.py +4 -1
  66. mlrun/datastore/hdfs.py +4 -1
  67. mlrun/datastore/inmem.py +4 -1
  68. mlrun/datastore/redis.py +4 -1
  69. mlrun/datastore/s3.py +4 -1
  70. mlrun/datastore/sources.py +52 -51
  71. mlrun/datastore/store_resources.py +0 -2
  72. mlrun/datastore/targets.py +21 -21
  73. mlrun/datastore/utils.py +2 -2
  74. mlrun/datastore/v3io.py +4 -1
  75. mlrun/datastore/vectorstore.py +194 -0
  76. mlrun/datastore/wasbfs/fs.py +13 -12
  77. mlrun/db/base.py +208 -82
  78. mlrun/db/factory.py +0 -3
  79. mlrun/db/httpdb.py +1237 -386
  80. mlrun/db/nopdb.py +201 -74
  81. mlrun/errors.py +2 -2
  82. mlrun/execution.py +136 -50
  83. mlrun/feature_store/__init__.py +0 -2
  84. mlrun/feature_store/api.py +41 -40
  85. mlrun/feature_store/common.py +9 -9
  86. mlrun/feature_store/feature_set.py +20 -18
  87. mlrun/feature_store/feature_vector.py +27 -24
  88. mlrun/feature_store/retrieval/base.py +14 -9
  89. mlrun/feature_store/retrieval/job.py +2 -1
  90. mlrun/feature_store/steps.py +2 -2
  91. mlrun/features.py +30 -13
  92. mlrun/frameworks/__init__.py +1 -2
  93. mlrun/frameworks/_common/__init__.py +1 -2
  94. mlrun/frameworks/_common/artifacts_library.py +2 -2
  95. mlrun/frameworks/_common/mlrun_interface.py +10 -6
  96. mlrun/frameworks/_common/model_handler.py +29 -27
  97. mlrun/frameworks/_common/producer.py +3 -1
  98. mlrun/frameworks/_dl_common/__init__.py +1 -2
  99. mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
  100. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
  101. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
  102. mlrun/frameworks/_ml_common/__init__.py +1 -2
  103. mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
  104. mlrun/frameworks/_ml_common/model_handler.py +21 -21
  105. mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
  106. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
  107. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  108. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  109. mlrun/frameworks/auto_mlrun/__init__.py +1 -2
  110. mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
  111. mlrun/frameworks/huggingface/__init__.py +1 -2
  112. mlrun/frameworks/huggingface/model_server.py +9 -9
  113. mlrun/frameworks/lgbm/__init__.py +47 -44
  114. mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
  115. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
  116. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
  117. mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
  118. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
  119. mlrun/frameworks/lgbm/model_handler.py +15 -11
  120. mlrun/frameworks/lgbm/model_server.py +11 -7
  121. mlrun/frameworks/lgbm/utils.py +2 -2
  122. mlrun/frameworks/onnx/__init__.py +1 -2
  123. mlrun/frameworks/onnx/dataset.py +3 -3
  124. mlrun/frameworks/onnx/mlrun_interface.py +2 -2
  125. mlrun/frameworks/onnx/model_handler.py +7 -5
  126. mlrun/frameworks/onnx/model_server.py +8 -6
  127. mlrun/frameworks/parallel_coordinates.py +11 -11
  128. mlrun/frameworks/pytorch/__init__.py +22 -23
  129. mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
  130. mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
  131. mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
  132. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
  133. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
  134. mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
  135. mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
  136. mlrun/frameworks/pytorch/model_handler.py +21 -17
  137. mlrun/frameworks/pytorch/model_server.py +13 -9
  138. mlrun/frameworks/sklearn/__init__.py +19 -18
  139. mlrun/frameworks/sklearn/estimator.py +2 -2
  140. mlrun/frameworks/sklearn/metric.py +3 -3
  141. mlrun/frameworks/sklearn/metrics_library.py +8 -6
  142. mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
  143. mlrun/frameworks/sklearn/model_handler.py +4 -3
  144. mlrun/frameworks/tf_keras/__init__.py +11 -12
  145. mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
  146. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
  147. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
  148. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
  149. mlrun/frameworks/tf_keras/model_handler.py +17 -13
  150. mlrun/frameworks/tf_keras/model_server.py +12 -8
  151. mlrun/frameworks/xgboost/__init__.py +19 -18
  152. mlrun/frameworks/xgboost/model_handler.py +13 -9
  153. mlrun/launcher/base.py +3 -4
  154. mlrun/launcher/local.py +1 -1
  155. mlrun/launcher/remote.py +1 -1
  156. mlrun/lists.py +4 -3
  157. mlrun/model.py +117 -46
  158. mlrun/model_monitoring/__init__.py +4 -4
  159. mlrun/model_monitoring/api.py +61 -59
  160. mlrun/model_monitoring/applications/_application_steps.py +17 -17
  161. mlrun/model_monitoring/applications/base.py +165 -6
  162. mlrun/model_monitoring/applications/context.py +88 -37
  163. mlrun/model_monitoring/applications/evidently_base.py +0 -1
  164. mlrun/model_monitoring/applications/histogram_data_drift.py +43 -21
  165. mlrun/model_monitoring/applications/results.py +55 -3
  166. mlrun/model_monitoring/controller.py +207 -239
  167. mlrun/model_monitoring/db/__init__.py +0 -2
  168. mlrun/model_monitoring/db/_schedules.py +156 -0
  169. mlrun/model_monitoring/db/_stats.py +189 -0
  170. mlrun/model_monitoring/db/tsdb/base.py +78 -25
  171. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +61 -6
  172. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
  173. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +255 -29
  174. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
  175. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +78 -17
  176. mlrun/model_monitoring/helpers.py +152 -49
  177. mlrun/model_monitoring/stream_processing.py +99 -283
  178. mlrun/model_monitoring/tracking_policy.py +10 -3
  179. mlrun/model_monitoring/writer.py +48 -36
  180. mlrun/package/__init__.py +3 -6
  181. mlrun/package/context_handler.py +1 -1
  182. mlrun/package/packager.py +12 -9
  183. mlrun/package/packagers/__init__.py +0 -2
  184. mlrun/package/packagers/default_packager.py +14 -11
  185. mlrun/package/packagers/numpy_packagers.py +16 -7
  186. mlrun/package/packagers/pandas_packagers.py +18 -18
  187. mlrun/package/packagers/python_standard_library_packagers.py +25 -11
  188. mlrun/package/packagers_manager.py +31 -14
  189. mlrun/package/utils/__init__.py +0 -3
  190. mlrun/package/utils/_pickler.py +6 -6
  191. mlrun/platforms/__init__.py +47 -16
  192. mlrun/platforms/iguazio.py +4 -1
  193. mlrun/projects/operations.py +27 -27
  194. mlrun/projects/pipelines.py +71 -36
  195. mlrun/projects/project.py +865 -206
  196. mlrun/run.py +53 -10
  197. mlrun/runtimes/__init__.py +1 -3
  198. mlrun/runtimes/base.py +15 -11
  199. mlrun/runtimes/daskjob.py +9 -9
  200. mlrun/runtimes/generators.py +2 -1
  201. mlrun/runtimes/kubejob.py +4 -5
  202. mlrun/runtimes/mounts.py +572 -0
  203. mlrun/runtimes/mpijob/__init__.py +0 -2
  204. mlrun/runtimes/mpijob/abstract.py +7 -6
  205. mlrun/runtimes/nuclio/api_gateway.py +7 -7
  206. mlrun/runtimes/nuclio/application/application.py +11 -11
  207. mlrun/runtimes/nuclio/function.py +19 -17
  208. mlrun/runtimes/nuclio/serving.py +18 -11
  209. mlrun/runtimes/pod.py +154 -45
  210. mlrun/runtimes/remotesparkjob.py +3 -2
  211. mlrun/runtimes/sparkjob/__init__.py +0 -2
  212. mlrun/runtimes/sparkjob/spark3job.py +21 -11
  213. mlrun/runtimes/utils.py +6 -5
  214. mlrun/serving/merger.py +6 -4
  215. mlrun/serving/remote.py +18 -17
  216. mlrun/serving/routers.py +185 -172
  217. mlrun/serving/server.py +7 -1
  218. mlrun/serving/states.py +97 -78
  219. mlrun/serving/utils.py +13 -2
  220. mlrun/serving/v1_serving.py +3 -2
  221. mlrun/serving/v2_serving.py +74 -65
  222. mlrun/track/__init__.py +1 -1
  223. mlrun/track/tracker.py +2 -2
  224. mlrun/track/trackers/mlflow_tracker.py +6 -5
  225. mlrun/utils/async_http.py +1 -1
  226. mlrun/utils/clones.py +1 -1
  227. mlrun/utils/helpers.py +54 -16
  228. mlrun/utils/logger.py +106 -4
  229. mlrun/utils/notifications/notification/__init__.py +22 -19
  230. mlrun/utils/notifications/notification/base.py +33 -14
  231. mlrun/utils/notifications/notification/console.py +6 -6
  232. mlrun/utils/notifications/notification/git.py +11 -11
  233. mlrun/utils/notifications/notification/ipython.py +10 -9
  234. mlrun/utils/notifications/notification/mail.py +176 -0
  235. mlrun/utils/notifications/notification/slack.py +6 -6
  236. mlrun/utils/notifications/notification/webhook.py +6 -6
  237. mlrun/utils/notifications/notification_pusher.py +86 -44
  238. mlrun/utils/regex.py +3 -1
  239. mlrun/utils/version/version.json +2 -2
  240. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/METADATA +21 -16
  241. mlrun-1.8.0rc8.dist-info/RECORD +347 -0
  242. mlrun/model_monitoring/db/stores/__init__.py +0 -136
  243. mlrun/model_monitoring/db/stores/base/store.py +0 -213
  244. mlrun/model_monitoring/db/stores/sqldb/__init__.py +0 -13
  245. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -71
  246. mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -190
  247. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -103
  248. mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -40
  249. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -659
  250. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +0 -13
  251. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -726
  252. mlrun/model_monitoring/model_endpoint.py +0 -118
  253. mlrun-1.7.1rc10.dist-info/RECORD +0 -351
  254. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/LICENSE +0 -0
  255. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/WHEEL +0 -0
  256. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc8.dist-info}/entry_points.txt +0 -0
  257. {mlrun-1.7.1rc10.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 ModelMonitoringApplicationMetric, ModelMonitoringApplicationResult
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, ModelMonitoringApplicationMetric
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
- data[mm_constant.ResultData.CURRENT_STATS] = json.dumps(
94
- application_context.sample_df_stats
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
- writer_event[mm_constant.WriterEvent.DATA] = json.dumps(data)
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.model_monitoring.ModelEndpoint] = {}
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 typing import Any, Union
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
- A base class for a model monitoring application.
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 for very simple custom application::
37
+ For example, :code:`MyApp` below is a simplistic custom application::
29
38
 
30
- class MyApp(ApplicationBase):
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, pad_features_hist
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
- from mlrun.model_monitoring.model_endpoint import ModelEndpoint
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
- Initialize a `MonitoringApplicationContext` object.
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: Dictionary of model endpoints.
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
- self.project_name = graph_context.project
82
- self.project = mlrun.load_project(url=self.project_name)
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="monitoring-application",
128
+ name=self._logger_name,
89
129
  )
90
130
  # Nuclio logger - `nuclio.request.Logger`.
91
- # Note: this logger does not accept keyword arguments.
92
- self.nuclio_logger = graph_context.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] = None
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] = None
116
- self._model_endpoint: Optional[ModelEndpoint] = model_endpoint_dict.get(
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
- return {
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.status.monitoring_feature_set_uri
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 = ModelEndpoint.from_flat_dict(
159
- get_endpoint_record(self.project_name, self.endpoint_id)
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 = json.loads(self.model_endpoint.status.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
- feature_names = self.model_endpoint.spec.feature_names
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
- label_names = self.model_endpoint.spec.label_names
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.project.log_artifact(
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.project.log_dataset(
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.project.log_model(
371
+ return self._artifacts_logger.log_model(
321
372
  key,
322
373
  body=body,
323
374
  framework=framework,
@@ -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)