mlrun 1.7.0rc5__py3-none-any.whl → 1.7.2__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 (234) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +39 -121
  3. mlrun/{datastore/helpers.py → alerts/__init__.py} +2 -5
  4. mlrun/alerts/alert.py +248 -0
  5. mlrun/api/schemas/__init__.py +4 -3
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +39 -254
  8. mlrun/artifacts/dataset.py +9 -190
  9. mlrun/artifacts/manager.py +73 -46
  10. mlrun/artifacts/model.py +30 -158
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +73 -2
  13. mlrun/common/db/sql_session.py +3 -2
  14. mlrun/common/formatters/__init__.py +21 -0
  15. mlrun/common/formatters/artifact.py +46 -0
  16. mlrun/common/formatters/base.py +113 -0
  17. mlrun/common/formatters/feature_set.py +44 -0
  18. mlrun/common/formatters/function.py +46 -0
  19. mlrun/common/formatters/pipeline.py +53 -0
  20. mlrun/common/formatters/project.py +51 -0
  21. mlrun/common/formatters/run.py +29 -0
  22. mlrun/common/helpers.py +11 -1
  23. mlrun/{runtimes → common/runtimes}/constants.py +32 -4
  24. mlrun/common/schemas/__init__.py +21 -4
  25. mlrun/common/schemas/alert.py +202 -0
  26. mlrun/common/schemas/api_gateway.py +113 -2
  27. mlrun/common/schemas/artifact.py +28 -1
  28. mlrun/common/schemas/auth.py +11 -0
  29. mlrun/common/schemas/client_spec.py +2 -1
  30. mlrun/common/schemas/common.py +7 -4
  31. mlrun/common/schemas/constants.py +3 -0
  32. mlrun/common/schemas/feature_store.py +58 -28
  33. mlrun/common/schemas/frontend_spec.py +8 -0
  34. mlrun/common/schemas/function.py +11 -0
  35. mlrun/common/schemas/hub.py +7 -9
  36. mlrun/common/schemas/model_monitoring/__init__.py +21 -4
  37. mlrun/common/schemas/model_monitoring/constants.py +136 -42
  38. mlrun/common/schemas/model_monitoring/grafana.py +9 -5
  39. mlrun/common/schemas/model_monitoring/model_endpoints.py +89 -41
  40. mlrun/common/schemas/notification.py +69 -12
  41. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  42. mlrun/common/schemas/pipeline.py +7 -0
  43. mlrun/common/schemas/project.py +67 -16
  44. mlrun/common/schemas/runs.py +17 -0
  45. mlrun/common/schemas/schedule.py +1 -1
  46. mlrun/common/schemas/workflow.py +10 -2
  47. mlrun/common/types.py +14 -1
  48. mlrun/config.py +224 -58
  49. mlrun/data_types/data_types.py +11 -1
  50. mlrun/data_types/spark.py +5 -4
  51. mlrun/data_types/to_pandas.py +75 -34
  52. mlrun/datastore/__init__.py +8 -10
  53. mlrun/datastore/alibaba_oss.py +131 -0
  54. mlrun/datastore/azure_blob.py +131 -43
  55. mlrun/datastore/base.py +107 -47
  56. mlrun/datastore/datastore.py +17 -7
  57. mlrun/datastore/datastore_profile.py +91 -7
  58. mlrun/datastore/dbfs_store.py +3 -7
  59. mlrun/datastore/filestore.py +1 -3
  60. mlrun/datastore/google_cloud_storage.py +92 -32
  61. mlrun/datastore/hdfs.py +5 -0
  62. mlrun/datastore/inmem.py +6 -3
  63. mlrun/datastore/redis.py +3 -2
  64. mlrun/datastore/s3.py +30 -12
  65. mlrun/datastore/snowflake_utils.py +45 -0
  66. mlrun/datastore/sources.py +274 -59
  67. mlrun/datastore/spark_utils.py +30 -0
  68. mlrun/datastore/store_resources.py +9 -7
  69. mlrun/datastore/storeytargets.py +151 -0
  70. mlrun/datastore/targets.py +374 -102
  71. mlrun/datastore/utils.py +68 -5
  72. mlrun/datastore/v3io.py +28 -50
  73. mlrun/db/auth_utils.py +152 -0
  74. mlrun/db/base.py +231 -22
  75. mlrun/db/factory.py +1 -4
  76. mlrun/db/httpdb.py +864 -228
  77. mlrun/db/nopdb.py +268 -16
  78. mlrun/errors.py +35 -5
  79. mlrun/execution.py +111 -38
  80. mlrun/feature_store/__init__.py +0 -2
  81. mlrun/feature_store/api.py +46 -53
  82. mlrun/feature_store/common.py +6 -11
  83. mlrun/feature_store/feature_set.py +48 -23
  84. mlrun/feature_store/feature_vector.py +13 -2
  85. mlrun/feature_store/ingestion.py +7 -6
  86. mlrun/feature_store/retrieval/base.py +9 -4
  87. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  88. mlrun/feature_store/retrieval/job.py +13 -4
  89. mlrun/feature_store/retrieval/local_merger.py +2 -0
  90. mlrun/feature_store/retrieval/spark_merger.py +24 -32
  91. mlrun/feature_store/steps.py +38 -19
  92. mlrun/features.py +6 -14
  93. mlrun/frameworks/_common/plan.py +3 -3
  94. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
  95. mlrun/frameworks/_ml_common/plan.py +1 -1
  96. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  97. mlrun/frameworks/lgbm/__init__.py +1 -1
  98. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  99. mlrun/frameworks/lgbm/model_handler.py +1 -1
  100. mlrun/frameworks/parallel_coordinates.py +4 -4
  101. mlrun/frameworks/pytorch/__init__.py +2 -2
  102. mlrun/frameworks/sklearn/__init__.py +1 -1
  103. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  104. mlrun/frameworks/tf_keras/__init__.py +5 -2
  105. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  106. mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
  107. mlrun/frameworks/xgboost/__init__.py +1 -1
  108. mlrun/k8s_utils.py +57 -12
  109. mlrun/launcher/__init__.py +1 -1
  110. mlrun/launcher/base.py +6 -5
  111. mlrun/launcher/client.py +13 -11
  112. mlrun/launcher/factory.py +1 -1
  113. mlrun/launcher/local.py +15 -5
  114. mlrun/launcher/remote.py +10 -3
  115. mlrun/lists.py +6 -2
  116. mlrun/model.py +297 -48
  117. mlrun/model_monitoring/__init__.py +1 -1
  118. mlrun/model_monitoring/api.py +152 -357
  119. mlrun/model_monitoring/applications/__init__.py +10 -0
  120. mlrun/model_monitoring/applications/_application_steps.py +190 -0
  121. mlrun/model_monitoring/applications/base.py +108 -0
  122. mlrun/model_monitoring/applications/context.py +341 -0
  123. mlrun/model_monitoring/{evidently_application.py → applications/evidently_base.py} +27 -22
  124. mlrun/model_monitoring/applications/histogram_data_drift.py +227 -91
  125. mlrun/model_monitoring/applications/results.py +99 -0
  126. mlrun/model_monitoring/controller.py +130 -303
  127. mlrun/model_monitoring/{stores/models/sqlite.py → db/__init__.py} +5 -10
  128. mlrun/model_monitoring/db/stores/__init__.py +136 -0
  129. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  130. mlrun/model_monitoring/db/stores/base/store.py +213 -0
  131. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  132. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  133. mlrun/model_monitoring/db/stores/sqldb/models/base.py +190 -0
  134. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +103 -0
  135. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  136. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +659 -0
  137. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  138. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +726 -0
  139. mlrun/model_monitoring/db/tsdb/__init__.py +105 -0
  140. mlrun/model_monitoring/db/tsdb/base.py +448 -0
  141. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  142. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  143. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +298 -0
  144. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +42 -0
  145. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +522 -0
  146. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  147. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +158 -0
  148. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +849 -0
  149. mlrun/model_monitoring/features_drift_table.py +34 -22
  150. mlrun/model_monitoring/helpers.py +177 -39
  151. mlrun/model_monitoring/model_endpoint.py +3 -2
  152. mlrun/model_monitoring/stream_processing.py +165 -398
  153. mlrun/model_monitoring/tracking_policy.py +7 -1
  154. mlrun/model_monitoring/writer.py +161 -125
  155. mlrun/package/packagers/default_packager.py +2 -2
  156. mlrun/package/packagers_manager.py +1 -0
  157. mlrun/package/utils/_formatter.py +2 -2
  158. mlrun/platforms/__init__.py +11 -10
  159. mlrun/platforms/iguazio.py +67 -228
  160. mlrun/projects/__init__.py +6 -1
  161. mlrun/projects/operations.py +47 -20
  162. mlrun/projects/pipelines.py +396 -249
  163. mlrun/projects/project.py +1125 -414
  164. mlrun/render.py +28 -22
  165. mlrun/run.py +207 -180
  166. mlrun/runtimes/__init__.py +76 -11
  167. mlrun/runtimes/base.py +40 -14
  168. mlrun/runtimes/daskjob.py +9 -2
  169. mlrun/runtimes/databricks_job/databricks_runtime.py +1 -0
  170. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  171. mlrun/runtimes/funcdoc.py +1 -29
  172. mlrun/runtimes/kubejob.py +34 -128
  173. mlrun/runtimes/local.py +39 -10
  174. mlrun/runtimes/mpijob/__init__.py +0 -20
  175. mlrun/runtimes/mpijob/abstract.py +8 -8
  176. mlrun/runtimes/mpijob/v1.py +1 -1
  177. mlrun/runtimes/nuclio/api_gateway.py +646 -177
  178. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  179. mlrun/runtimes/nuclio/application/application.py +758 -0
  180. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  181. mlrun/runtimes/nuclio/function.py +188 -68
  182. mlrun/runtimes/nuclio/serving.py +57 -60
  183. mlrun/runtimes/pod.py +191 -58
  184. mlrun/runtimes/remotesparkjob.py +11 -8
  185. mlrun/runtimes/sparkjob/spark3job.py +17 -18
  186. mlrun/runtimes/utils.py +40 -73
  187. mlrun/secrets.py +6 -2
  188. mlrun/serving/__init__.py +8 -1
  189. mlrun/serving/remote.py +2 -3
  190. mlrun/serving/routers.py +89 -64
  191. mlrun/serving/server.py +54 -26
  192. mlrun/serving/states.py +187 -56
  193. mlrun/serving/utils.py +19 -11
  194. mlrun/serving/v2_serving.py +136 -63
  195. mlrun/track/tracker.py +2 -1
  196. mlrun/track/trackers/mlflow_tracker.py +5 -0
  197. mlrun/utils/async_http.py +26 -6
  198. mlrun/utils/db.py +18 -0
  199. mlrun/utils/helpers.py +375 -105
  200. mlrun/utils/http.py +2 -2
  201. mlrun/utils/logger.py +75 -9
  202. mlrun/utils/notifications/notification/__init__.py +14 -10
  203. mlrun/utils/notifications/notification/base.py +48 -0
  204. mlrun/utils/notifications/notification/console.py +2 -0
  205. mlrun/utils/notifications/notification/git.py +24 -1
  206. mlrun/utils/notifications/notification/ipython.py +2 -0
  207. mlrun/utils/notifications/notification/slack.py +96 -21
  208. mlrun/utils/notifications/notification/webhook.py +63 -2
  209. mlrun/utils/notifications/notification_pusher.py +146 -16
  210. mlrun/utils/regex.py +9 -0
  211. mlrun/utils/retryer.py +3 -2
  212. mlrun/utils/v3io_clients.py +2 -3
  213. mlrun/utils/version/version.json +2 -2
  214. mlrun-1.7.2.dist-info/METADATA +390 -0
  215. mlrun-1.7.2.dist-info/RECORD +351 -0
  216. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/WHEEL +1 -1
  217. mlrun/feature_store/retrieval/conversion.py +0 -271
  218. mlrun/kfpops.py +0 -868
  219. mlrun/model_monitoring/application.py +0 -310
  220. mlrun/model_monitoring/batch.py +0 -974
  221. mlrun/model_monitoring/controller_handler.py +0 -37
  222. mlrun/model_monitoring/prometheus.py +0 -216
  223. mlrun/model_monitoring/stores/__init__.py +0 -111
  224. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +0 -574
  225. mlrun/model_monitoring/stores/model_endpoint_store.py +0 -145
  226. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  227. mlrun/model_monitoring/stores/models/base.py +0 -84
  228. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  229. mlrun/platforms/other.py +0 -305
  230. mlrun-1.7.0rc5.dist-info/METADATA +0 -269
  231. mlrun-1.7.0rc5.dist-info/RECORD +0 -323
  232. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/LICENSE +0 -0
  233. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/entry_points.txt +0 -0
  234. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/top_level.txt +0 -0
@@ -13,8 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import hashlib
16
- import json
17
16
  import typing
17
+ import warnings
18
18
  from datetime import datetime
19
19
 
20
20
  import numpy as np
@@ -22,13 +22,13 @@ import pandas as pd
22
22
 
23
23
  import mlrun.artifacts
24
24
  import mlrun.common.helpers
25
+ import mlrun.common.schemas.model_monitoring.constants as mm_constants
25
26
  import mlrun.feature_store
26
- from mlrun.common.schemas.model_monitoring import EventFieldType, ModelMonitoringMode
27
+ import mlrun.model_monitoring.applications as mm_app
28
+ import mlrun.serving
27
29
  from mlrun.data_types.infer import InferOptions, get_df_stats
28
30
  from mlrun.utils import datetime_now, logger
29
31
 
30
- from .batch import VirtualDrift
31
- from .features_drift_table import FeaturesDriftTablePlot
32
32
  from .helpers import update_model_endpoint_last_request
33
33
  from .model_endpoint import ModelEndpoint
34
34
 
@@ -46,9 +46,9 @@ def get_or_create_model_endpoint(
46
46
  function_name: str = "",
47
47
  context: mlrun.MLClientCtx = None,
48
48
  sample_set_statistics: dict[str, typing.Any] = None,
49
- drift_threshold: float = None,
50
- possible_drift_threshold: float = None,
51
- monitoring_mode: ModelMonitoringMode = ModelMonitoringMode.disabled,
49
+ drift_threshold: typing.Optional[float] = None,
50
+ possible_drift_threshold: typing.Optional[float] = None,
51
+ monitoring_mode: mm_constants.ModelMonitoringMode = mm_constants.ModelMonitoringMode.disabled,
52
52
  db_session=None,
53
53
  ) -> ModelEndpoint:
54
54
  """
@@ -68,14 +68,14 @@ def get_or_create_model_endpoint(
68
68
  full function hash.
69
69
  :param sample_set_statistics: Dictionary of sample set statistics that will be used as a reference data for
70
70
  the new model endpoint (applicable only to new endpoint_id).
71
- :param drift_threshold: The threshold of which to mark drifts (applicable only to new endpoint_id).
72
- :param possible_drift_threshold: The threshold of which to mark possible drifts (applicable only to new
71
+ :param drift_threshold: (deprecated) The threshold of which to mark drifts (applicable only to new
72
+ endpoint_id).
73
+ :param possible_drift_threshold: (deprecated) The threshold of which to mark possible drifts (applicable only to new
73
74
  endpoint_id).
74
75
  :param monitoring_mode: If enabled, apply model monitoring features on the provided endpoint id
75
76
  (applicable only to new endpoint_id).
76
77
  :param db_session: A runtime session that manages the current dialog with the database.
77
78
 
78
-
79
79
  :return: A ModelEndpoint object
80
80
  """
81
81
 
@@ -97,8 +97,6 @@ def get_or_create_model_endpoint(
97
97
  model_endpoint=model_endpoint,
98
98
  model_path=model_path,
99
99
  sample_set_statistics=sample_set_statistics,
100
- drift_threshold=drift_threshold,
101
- possible_drift_threshold=possible_drift_threshold,
102
100
  )
103
101
 
104
102
  except mlrun.errors.MLRunNotFoundError:
@@ -112,8 +110,6 @@ def get_or_create_model_endpoint(
112
110
  function_name=function_name,
113
111
  context=context,
114
112
  sample_set_statistics=sample_set_statistics,
115
- drift_threshold=drift_threshold,
116
- possible_drift_threshold=possible_drift_threshold,
117
113
  monitoring_mode=monitoring_mode,
118
114
  )
119
115
  return model_endpoint
@@ -128,20 +124,19 @@ def record_results(
128
124
  context: typing.Optional[mlrun.MLClientCtx] = None,
129
125
  infer_results_df: typing.Optional[pd.DataFrame] = None,
130
126
  sample_set_statistics: typing.Optional[dict[str, typing.Any]] = None,
131
- monitoring_mode: ModelMonitoringMode = ModelMonitoringMode.enabled,
127
+ monitoring_mode: mm_constants.ModelMonitoringMode = mm_constants.ModelMonitoringMode.enabled,
128
+ # Deprecated arguments:
132
129
  drift_threshold: typing.Optional[float] = None,
133
130
  possible_drift_threshold: typing.Optional[float] = None,
134
131
  trigger_monitoring_job: bool = False,
135
132
  artifacts_tag: str = "",
136
- default_batch_image="mlrun/mlrun",
133
+ default_batch_image: str = "mlrun/mlrun",
137
134
  ) -> ModelEndpoint:
138
135
  """
139
136
  Write a provided inference dataset to model endpoint parquet target. If not exist, generate a new model endpoint
140
137
  record and use the provided sample set statistics as feature stats that will be used later for the drift analysis.
141
- To manually trigger the monitoring batch job, set `trigger_monitoring_job=True` and then the batch
142
- job will immediately perform drift analysis between the sample set statistics stored in the model and the new
143
- input data (along with the outputs). The drift rule is the value per-feature mean of the TVD and Hellinger scores
144
- according to the provided thresholds.
138
+ To activate model monitoring, run `project.enable_model_monitoring()`. The model monitoring applications will be
139
+ triggered with the recorded data according to a periodic schedule.
145
140
 
146
141
  :param project: Project name.
147
142
  :param model_path: The model Store path.
@@ -151,8 +146,7 @@ def record_results(
151
146
  on the provided `endpoint_id`.
152
147
  :param function_name: If a new model endpoint is created, use this function name for generating the
153
148
  function URI.
154
- :param context: MLRun context. Note that the context is required for logging the artifacts
155
- following the batch drift job.
149
+ :param context: MLRun context. Note that the context is required generating the model endpoint.
156
150
  :param infer_results_df: DataFrame that will be stored under the model endpoint parquet target. Will be
157
151
  used for doing the drift analysis. Please make sure that the dataframe includes
158
152
  both feature names and label columns.
@@ -160,17 +154,47 @@ def record_results(
160
154
  the current model endpoint.
161
155
  :param monitoring_mode: If enabled, apply model monitoring features on the provided endpoint id. Enabled
162
156
  by default.
163
- :param drift_threshold: The threshold of which to mark drifts.
164
- :param possible_drift_threshold: The threshold of which to mark possible drifts.
165
- :param trigger_monitoring_job: If true, run the batch drift job. If not exists, the monitoring batch function
166
- will be registered through MLRun API with the provided image.
167
- :param artifacts_tag: Tag to use for all the artifacts resulted from the function. Will be relevant
168
- only if the monitoring batch job has been triggered.
169
-
170
- :param default_batch_image: The image that will be used when registering the model monitoring batch job.
157
+ :param drift_threshold: (deprecated) The threshold of which to mark drifts.
158
+ :param possible_drift_threshold: (deprecated) The threshold of which to mark possible drifts.
159
+ :param trigger_monitoring_job: (deprecated) If true, run the batch drift job. If not exists, the monitoring
160
+ batch function will be registered through MLRun API with the provided image.
161
+ :param artifacts_tag: (deprecated) Tag to use for all the artifacts resulted from the function.
162
+ Will be relevant only if the monitoring batch job has been triggered.
163
+ :param default_batch_image: (deprecated) The image that will be used when registering the model monitoring
164
+ batch job.
171
165
 
172
166
  :return: A ModelEndpoint object
173
167
  """
168
+
169
+ if drift_threshold is not None or possible_drift_threshold is not None:
170
+ warnings.warn(
171
+ "Custom drift threshold arguments are deprecated since version "
172
+ "1.7.0 and have no effect. They will be removed in version 1.9.0.\n"
173
+ "To enable the default histogram data drift application, run:\n"
174
+ "`project.enable_model_monitoring()`.",
175
+ FutureWarning,
176
+ )
177
+ if trigger_monitoring_job is not False:
178
+ warnings.warn(
179
+ "`trigger_monitoring_job` argument is deprecated since version "
180
+ "1.7.0 and has no effect. It will be removed in version 1.9.0.\n"
181
+ "To enable the default histogram data drift application, run:\n"
182
+ "`project.enable_model_monitoring()`.",
183
+ FutureWarning,
184
+ )
185
+ if artifacts_tag != "":
186
+ warnings.warn(
187
+ "`artifacts_tag` argument is deprecated since version "
188
+ "1.7.0 and has no effect. It will be removed in version 1.9.0.",
189
+ FutureWarning,
190
+ )
191
+ if default_batch_image != "mlrun/mlrun":
192
+ warnings.warn(
193
+ "`default_batch_image` argument is deprecated since version "
194
+ "1.7.0 and has no effect. It will be removed in version 1.9.0.",
195
+ FutureWarning,
196
+ )
197
+
174
198
  db = mlrun.get_run_db()
175
199
 
176
200
  model_endpoint = get_or_create_model_endpoint(
@@ -181,8 +205,6 @@ def record_results(
181
205
  function_name=function_name,
182
206
  context=context,
183
207
  sample_set_statistics=sample_set_statistics,
184
- drift_threshold=drift_threshold,
185
- possible_drift_threshold=possible_drift_threshold,
186
208
  monitoring_mode=monitoring_mode,
187
209
  db_session=db,
188
210
  )
@@ -206,33 +228,6 @@ def record_results(
206
228
  db=db,
207
229
  )
208
230
 
209
- if trigger_monitoring_job:
210
- # Run the monitoring batch drift job
211
- trigger_drift_batch_job(
212
- project=project,
213
- default_batch_image=default_batch_image,
214
- model_endpoints_ids=[model_endpoint.metadata.uid],
215
- db_session=db,
216
- )
217
-
218
- # Getting drift thresholds if not provided
219
- drift_threshold, possible_drift_threshold = get_drift_thresholds_if_not_none(
220
- model_endpoint=model_endpoint,
221
- drift_threshold=drift_threshold,
222
- possible_drift_threshold=possible_drift_threshold,
223
- )
224
-
225
- perform_drift_analysis(
226
- project=project,
227
- context=context,
228
- sample_set_statistics=model_endpoint.status.feature_stats,
229
- drift_threshold=drift_threshold,
230
- possible_drift_threshold=possible_drift_threshold,
231
- artifacts_tag=artifacts_tag,
232
- endpoint_id=model_endpoint.metadata.uid,
233
- db_session=db,
234
- )
235
-
236
231
  return model_endpoint
237
232
 
238
233
 
@@ -240,9 +235,7 @@ def _model_endpoint_validations(
240
235
  model_endpoint: ModelEndpoint,
241
236
  model_path: str = "",
242
237
  sample_set_statistics: dict[str, typing.Any] = None,
243
- drift_threshold: float = None,
244
- possible_drift_threshold: float = None,
245
- ):
238
+ ) -> None:
246
239
  """
247
240
  Validate that provided model endpoint configurations match the stored fields of the provided `ModelEndpoint`
248
241
  object. Usually, this method is called by `get_or_create_model_endpoint()` in cases that the model endpoint
@@ -256,20 +249,32 @@ def _model_endpoint_validations(
256
249
  is forbidden to provide a different reference data to that model endpoint.
257
250
  In case of discrepancy between the provided `sample_set_statistics` and the
258
251
  `model_endpoints.spec.feature_stats`, a warning will be presented to the user.
259
- :param drift_threshold: The threshold of which to mark drifts. Should be similar to the drift threshold
260
- that has already assigned to the current model endpoint.
261
- :param possible_drift_threshold: The threshold of which to mark possible drifts. Should be similar to the possible
262
- drift threshold that has already assigned to the current model endpoint.
263
-
264
252
  """
265
- # Model path
266
- if model_path and model_endpoint.spec.model_uri != model_path:
267
- raise mlrun.errors.MLRunInvalidArgumentError(
268
- f"provided model store path {model_path} does not match "
269
- f"the path that is stored under the existing model "
270
- f"endpoint record: {model_endpoint.spec.model_uri}"
253
+
254
+ # Model Path
255
+ if model_path:
256
+ # Generate the parsed model uri that is based on hash, key, iter, and tree
257
+ model_obj = mlrun.datastore.get_store_resource(model_path)
258
+
259
+ model_artifact_uri = mlrun.utils.helpers.generate_artifact_uri(
260
+ project=model_endpoint.metadata.project,
261
+ key=model_obj.key,
262
+ iter=model_obj.iter,
263
+ tree=model_obj.tree,
271
264
  )
272
265
 
266
+ # Enrich the uri schema with the store prefix
267
+ model_artifact_uri = mlrun.datastore.get_store_uri(
268
+ kind=mlrun.utils.helpers.StorePrefix.Model, uri=model_artifact_uri
269
+ )
270
+
271
+ if model_endpoint.spec.model_uri != model_artifact_uri:
272
+ raise mlrun.errors.MLRunInvalidArgumentError(
273
+ f"provided model store path {model_path} does not match "
274
+ f"the path that is stored under the existing model "
275
+ f"endpoint record: {model_endpoint.spec.model_uri}"
276
+ )
277
+
273
278
  # Feature stats
274
279
  if (
275
280
  sample_set_statistics
@@ -279,62 +284,6 @@ def _model_endpoint_validations(
279
284
  "Provided sample set statistics is different from the registered statistics. "
280
285
  "If new sample set statistics is to be used, new model endpoint should be created"
281
286
  )
282
- # drift and possible drift thresholds
283
- if drift_threshold:
284
- current_drift_threshold = model_endpoint.spec.monitor_configuration.get(
285
- EventFieldType.DRIFT_DETECTED_THRESHOLD,
286
- mlrun.mlconf.model_endpoint_monitoring.drift_thresholds.default.drift_detected,
287
- )
288
- if current_drift_threshold != drift_threshold:
289
- raise mlrun.errors.MLRunInvalidArgumentError(
290
- f"Cannot change existing drift threshold. Expected {current_drift_threshold}, got {drift_threshold} "
291
- f"Please update drift threshold or generate a new model endpoint record"
292
- )
293
-
294
- if possible_drift_threshold:
295
- current_possible_drift_threshold = model_endpoint.spec.monitor_configuration.get(
296
- EventFieldType.POSSIBLE_DRIFT_THRESHOLD,
297
- mlrun.mlconf.model_endpoint_monitoring.drift_thresholds.default.possible_drift,
298
- )
299
- if current_possible_drift_threshold != possible_drift_threshold:
300
- raise mlrun.errors.MLRunInvalidArgumentError(
301
- f"Cannot change existing possible drift threshold. Expected {current_possible_drift_threshold}, "
302
- f"got {possible_drift_threshold}. Please update drift threshold or generate a new model endpoint record"
303
- )
304
-
305
-
306
- def get_drift_thresholds_if_not_none(
307
- model_endpoint: ModelEndpoint,
308
- drift_threshold: float = None,
309
- possible_drift_threshold: float = None,
310
- ) -> tuple[float, float]:
311
- """
312
- Get drift and possible drift thresholds. If one of the thresholds is missing, will try to retrieve
313
- it from the `ModelEndpoint` object. If not defined under the `ModelEndpoint` as well, will retrieve it from
314
- the default mlrun configuration.
315
-
316
- :param model_endpoint: `ModelEndpoint` object.
317
- :param drift_threshold: The threshold of which to mark drifts.
318
- :param possible_drift_threshold: The threshold of which to mark possible drifts.
319
-
320
- :return: A Tuple of:
321
- [0] drift threshold as a float
322
- [1] possible drift threshold as a float
323
- """
324
- if not drift_threshold:
325
- # Getting drift threshold value from either model endpoint or monitoring default configurations
326
- drift_threshold = model_endpoint.spec.monitor_configuration.get(
327
- EventFieldType.DRIFT_DETECTED_THRESHOLD,
328
- mlrun.mlconf.model_endpoint_monitoring.drift_thresholds.default.drift_detected,
329
- )
330
- if not possible_drift_threshold:
331
- # Getting possible drift threshold value from either model endpoint or monitoring default configurations
332
- possible_drift_threshold = model_endpoint.spec.monitor_configuration.get(
333
- EventFieldType.POSSIBLE_DRIFT_THRESHOLD,
334
- mlrun.mlconf.model_endpoint_monitoring.drift_thresholds.default.possible_drift,
335
- )
336
-
337
- return drift_threshold, possible_drift_threshold
338
287
 
339
288
 
340
289
  def write_monitoring_df(
@@ -366,14 +315,14 @@ def write_monitoring_df(
366
315
  )
367
316
 
368
317
  # Modify the DataFrame to the required structure that will be used later by the monitoring batch job
369
- if EventFieldType.TIMESTAMP not in infer_results_df.columns:
318
+ if mm_constants.EventFieldType.TIMESTAMP not in infer_results_df.columns:
370
319
  # Initialize timestamp column with the current time
371
- infer_results_df[EventFieldType.TIMESTAMP] = infer_datetime
320
+ infer_results_df[mm_constants.EventFieldType.TIMESTAMP] = infer_datetime
372
321
 
373
322
  # `endpoint_id` is the monitoring feature set entity and therefore it should be defined as the df index before
374
323
  # the ingest process
375
- infer_results_df[EventFieldType.ENDPOINT_ID] = endpoint_id
376
- infer_results_df.set_index(EventFieldType.ENDPOINT_ID, inplace=True)
324
+ infer_results_df[mm_constants.EventFieldType.ENDPOINT_ID] = endpoint_id
325
+ infer_results_df.set_index(mm_constants.EventFieldType.ENDPOINT_ID, inplace=True)
377
326
 
378
327
  monitoring_feature_set.ingest(source=infer_results_df, overwrite=False)
379
328
 
@@ -387,9 +336,7 @@ def _generate_model_endpoint(
387
336
  function_name: str,
388
337
  context: mlrun.MLClientCtx,
389
338
  sample_set_statistics: dict[str, typing.Any],
390
- drift_threshold: float,
391
- possible_drift_threshold: float,
392
- monitoring_mode: ModelMonitoringMode = ModelMonitoringMode.disabled,
339
+ monitoring_mode: mm_constants.ModelMonitoringMode = mm_constants.ModelMonitoringMode.disabled,
393
340
  ) -> ModelEndpoint:
394
341
  """
395
342
  Write a new model endpoint record.
@@ -407,8 +354,6 @@ def _generate_model_endpoint(
407
354
  :param sample_set_statistics: Dictionary of sample set statistics that will be used as a reference data for
408
355
  the current model endpoint. Will be stored under
409
356
  `model_endpoint.status.feature_stats`.
410
- :param drift_threshold: The threshold of which to mark drifts.
411
- :param possible_drift_threshold: The threshold of which to mark possible drifts.
412
357
 
413
358
  :return `mlrun.model_monitoring.model_endpoint.ModelEndpoint` object.
414
359
  """
@@ -426,15 +371,6 @@ def _generate_model_endpoint(
426
371
  model_endpoint.spec.model_uri = model_path
427
372
  model_endpoint.spec.model = model_endpoint_name
428
373
  model_endpoint.spec.model_class = "drift-analysis"
429
- if drift_threshold:
430
- model_endpoint.spec.monitor_configuration[
431
- EventFieldType.DRIFT_DETECTED_THRESHOLD
432
- ] = drift_threshold
433
- if possible_drift_threshold:
434
- model_endpoint.spec.monitor_configuration[
435
- EventFieldType.POSSIBLE_DRIFT_THRESHOLD
436
- ] = possible_drift_threshold
437
-
438
374
  model_endpoint.spec.monitoring_mode = monitoring_mode
439
375
  model_endpoint.status.first_request = model_endpoint.status.last_request = (
440
376
  datetime_now().isoformat()
@@ -449,71 +385,6 @@ def _generate_model_endpoint(
449
385
  return db_session.get_model_endpoint(project=project, endpoint_id=endpoint_id)
450
386
 
451
387
 
452
- def trigger_drift_batch_job(
453
- project: str,
454
- default_batch_image="mlrun/mlrun",
455
- model_endpoints_ids: list[str] = None,
456
- batch_intervals_dict: dict[str, float] = None,
457
- db_session=None,
458
- ):
459
- """
460
- Run model monitoring drift analysis job. If not exists, the monitoring batch function will be registered through
461
- MLRun API with the provided image.
462
-
463
- :param project: Project name.
464
- :param default_batch_image: The image that will be used when registering the model monitoring batch job.
465
- :param model_endpoints_ids: List of model endpoints to include in the current run.
466
- :param batch_intervals_dict: Batch interval range (days, hours, minutes). By default, the batch interval is
467
- configured to run through the last hour.
468
- :param db_session: A runtime session that manages the current dialog with the database.
469
-
470
- """
471
- if not model_endpoints_ids:
472
- raise mlrun.errors.MLRunNotFoundError(
473
- "No model endpoints provided",
474
- )
475
- if not db_session:
476
- db_session = mlrun.get_run_db()
477
-
478
- # Register the monitoring batch job (do nothing if already exist) and get the job function as a dictionary
479
- batch_function_dict: dict[str, typing.Any] = db_session.deploy_monitoring_batch_job(
480
- project=project,
481
- default_batch_image=default_batch_image,
482
- )
483
-
484
- # Prepare current run params
485
- job_params = _generate_job_params(
486
- model_endpoints_ids=model_endpoints_ids,
487
- batch_intervals_dict=batch_intervals_dict,
488
- )
489
-
490
- # Generate runtime and trigger the job function
491
- batch_function = mlrun.new_function(runtime=batch_function_dict)
492
- batch_function.run(name="model-monitoring-batch", params=job_params, watch=True)
493
-
494
-
495
- def _generate_job_params(
496
- model_endpoints_ids: list[str],
497
- batch_intervals_dict: dict[str, float] = None,
498
- ):
499
- """
500
- Generate the required params for the model monitoring batch job function.
501
-
502
- :param model_endpoints_ids: List of model endpoints to include in the current run.
503
- :param batch_intervals_dict: Batch interval range (days, hours, minutes). By default, the batch interval is
504
- configured to run through the last hour.
505
-
506
- """
507
- if not batch_intervals_dict:
508
- # Generate default batch intervals dict
509
- batch_intervals_dict = {"minutes": 0, "hours": 1, "days": 0}
510
-
511
- return {
512
- "model_endpoints": model_endpoints_ids,
513
- "batch_intervals_dict": batch_intervals_dict,
514
- }
515
-
516
-
517
388
  def get_sample_set_statistics(
518
389
  sample_set: DatasetType = None,
519
390
  model_artifact_feature_stats: dict = None,
@@ -659,151 +530,6 @@ def read_dataset_as_dataframe(
659
530
  return dataset, label_columns
660
531
 
661
532
 
662
- def perform_drift_analysis(
663
- project: str,
664
- endpoint_id: str,
665
- context: mlrun.MLClientCtx,
666
- sample_set_statistics: dict,
667
- drift_threshold: float,
668
- possible_drift_threshold: float,
669
- artifacts_tag: str = "",
670
- db_session=None,
671
- ) -> None:
672
- """
673
- Calculate drift per feature and produce the drift table artifact for logging post prediction. Note that most of
674
- the calculations were already made through the monitoring batch job.
675
-
676
- :param project: Project name.
677
- :param endpoint_id: Model endpoint unique ID.
678
- :param context: MLRun context. Will log the artifacts.
679
- :param sample_set_statistics: The statistics of the sample set logged along a model.
680
- :param drift_threshold: The threshold of which to mark drifts.
681
- :param possible_drift_threshold: The threshold of which to mark possible drifts.
682
- :param artifacts_tag: Tag to use for all the artifacts resulted from the function.
683
- :param db_session: A runtime session that manages the current dialog with the database.
684
-
685
- """
686
- if not db_session:
687
- db_session = mlrun.get_run_db()
688
-
689
- model_endpoint = db_session.get_model_endpoint(
690
- project=project, endpoint_id=endpoint_id
691
- )
692
-
693
- # Get the drift metrics results along with the feature statistics from the latest batch
694
- metrics = model_endpoint.status.drift_measures
695
- inputs_statistics = model_endpoint.status.current_stats
696
-
697
- inputs_statistics.pop(EventFieldType.TIMESTAMP, None)
698
-
699
- # Calculate drift for each feature
700
- virtual_drift = VirtualDrift()
701
- drift_results = virtual_drift.check_for_drift_per_feature(
702
- metrics_results_dictionary=metrics,
703
- possible_drift_threshold=possible_drift_threshold,
704
- drift_detected_threshold=drift_threshold,
705
- )
706
-
707
- # Drift table plot
708
- html_plot = FeaturesDriftTablePlot().produce(
709
- sample_set_statistics=sample_set_statistics,
710
- inputs_statistics=inputs_statistics,
711
- metrics=metrics,
712
- drift_results=drift_results,
713
- )
714
-
715
- # Prepare drift result per feature dictionary
716
- metrics_per_feature = {
717
- feature: _get_drift_result(
718
- tvd=metric_dictionary["tvd"],
719
- hellinger=metric_dictionary["hellinger"],
720
- threshold=drift_threshold,
721
- )[1]
722
- for feature, metric_dictionary in metrics.items()
723
- if isinstance(metric_dictionary, dict)
724
- }
725
-
726
- # Calculate the final analysis result as well
727
- drift_status, drift_metric = _get_drift_result(
728
- tvd=metrics["tvd_mean"],
729
- hellinger=metrics["hellinger_mean"],
730
- threshold=drift_threshold,
731
- )
732
- # Log the different artifacts
733
- _log_drift_artifacts(
734
- context=context,
735
- html_plot=html_plot,
736
- metrics_per_feature=metrics_per_feature,
737
- drift_status=drift_status,
738
- drift_metric=drift_metric,
739
- artifacts_tag=artifacts_tag,
740
- )
741
-
742
-
743
- def _log_drift_artifacts(
744
- context: mlrun.MLClientCtx,
745
- html_plot: str,
746
- metrics_per_feature: dict[str, float],
747
- drift_status: bool,
748
- drift_metric: float,
749
- artifacts_tag: str,
750
- ):
751
- """
752
- Log the following artifacts/results:
753
- 1 - Drift table plot which includes a detailed drift analysis per feature
754
- 2 - Drift result per feature in a JSON format
755
- 3 - Results of the total drift analysis
756
-
757
- :param context: MLRun context. Will log the artifacts.
758
- :param html_plot: Body of the html file of the plot.
759
- :param metrics_per_feature: Dictionary in which the key is a feature name and the value is the drift numerical
760
- result.
761
- :param drift_status: Boolean value that represents the final drift analysis result.
762
- :param drift_metric: The final drift numerical result.
763
- :param artifacts_tag: Tag to use for all the artifacts resulted from the function.
764
-
765
- """
766
- context.log_artifact(
767
- mlrun.artifacts.Artifact(
768
- body=html_plot.encode("utf-8"), format="html", key="drift_table_plot"
769
- ),
770
- tag=artifacts_tag,
771
- )
772
- context.log_artifact(
773
- mlrun.artifacts.Artifact(
774
- body=json.dumps(metrics_per_feature),
775
- format="json",
776
- key="features_drift_results",
777
- ),
778
- tag=artifacts_tag,
779
- )
780
- context.log_results(
781
- results={"drift_status": drift_status, "drift_metric": drift_metric}
782
- )
783
-
784
-
785
- def _get_drift_result(
786
- tvd: float,
787
- hellinger: float,
788
- threshold: float,
789
- ) -> tuple[bool, float]:
790
- """
791
- Calculate the drift result by the following equation: (tvd + hellinger) / 2
792
-
793
- :param tvd: The feature's TVD value.
794
- :param hellinger: The feature's Hellinger value.
795
- :param threshold: The threshold from which the value is considered a drift.
796
-
797
- :returns: A tuple of:
798
- [0] = Boolean value as the drift status.
799
- [1] = The result.
800
- """
801
- result = (tvd + hellinger) / 2
802
- if result >= threshold:
803
- return True, result
804
- return False, result
805
-
806
-
807
533
  def log_result(
808
534
  context: mlrun.MLClientCtx,
809
535
  result_set_name: str,
@@ -826,3 +552,72 @@ def log_result(
826
552
  key="batch_id",
827
553
  value=batch_id,
828
554
  )
555
+
556
+
557
+ def _create_model_monitoring_function_base(
558
+ *,
559
+ project: str,
560
+ func: typing.Union[str, None] = None,
561
+ application_class: typing.Union[
562
+ str,
563
+ mm_app.ModelMonitoringApplicationBase,
564
+ None,
565
+ ] = None,
566
+ name: typing.Optional[str] = None,
567
+ image: typing.Optional[str] = None,
568
+ tag: typing.Optional[str] = None,
569
+ requirements: typing.Union[str, list[str], None] = None,
570
+ requirements_file: str = "",
571
+ **application_kwargs,
572
+ ) -> mlrun.runtimes.ServingRuntime:
573
+ """
574
+ Note: this is an internal API only.
575
+ This function does not set the labels or mounts v3io.
576
+ """
577
+ if name in mm_constants._RESERVED_FUNCTION_NAMES:
578
+ raise mlrun.errors.MLRunInvalidArgumentError(
579
+ "An application cannot have the following names: "
580
+ f"{mm_constants._RESERVED_FUNCTION_NAMES}"
581
+ )
582
+ if func is None:
583
+ func = ""
584
+ func_obj = typing.cast(
585
+ mlrun.runtimes.ServingRuntime,
586
+ mlrun.code_to_function(
587
+ filename=func,
588
+ name=name,
589
+ project=project,
590
+ tag=tag,
591
+ kind=mlrun.run.RuntimeKinds.serving,
592
+ image=image,
593
+ requirements=requirements,
594
+ requirements_file=requirements_file,
595
+ ),
596
+ )
597
+ graph = func_obj.set_topology(mlrun.serving.states.StepKinds.flow)
598
+ prepare_step = graph.to(
599
+ class_name="mlrun.model_monitoring.applications._application_steps._PrepareMonitoringEvent",
600
+ name="PrepareMonitoringEvent",
601
+ application_name=name,
602
+ )
603
+ if isinstance(application_class, str):
604
+ app_step = prepare_step.to(class_name=application_class, **application_kwargs)
605
+ else:
606
+ app_step = prepare_step.to(class_name=application_class)
607
+
608
+ app_step.__class__ = mlrun.serving.MonitoringApplicationStep
609
+
610
+ app_step.error_handler(
611
+ name="ApplicationErrorHandler",
612
+ class_name="mlrun.model_monitoring.applications._application_steps._ApplicationErrorHandler",
613
+ full_event=True,
614
+ project=project,
615
+ )
616
+
617
+ app_step.to(
618
+ class_name="mlrun.model_monitoring.applications._application_steps._PushToMonitoringWriter",
619
+ name="PushToMonitoringWriter",
620
+ project=project,
621
+ writer_application_name=mm_constants.MonitoringFunctionNames.WRITER,
622
+ )
623
+ return func_obj
@@ -11,3 +11,13 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ #
15
+
16
+ from .base import ModelMonitoringApplicationBase
17
+ from .context import MonitoringApplicationContext
18
+ from .evidently_base import (
19
+ _HAS_EVIDENTLY,
20
+ SUPPORTED_EVIDENTLY_VERSION,
21
+ EvidentlyModelMonitoringApplicationBase,
22
+ )
23
+ from .results import ModelMonitoringApplicationMetric, ModelMonitoringApplicationResult