mlrun 1.7.0rc4__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 (235) 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 -1
  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 +31 -4
  25. mlrun/common/schemas/alert.py +202 -0
  26. mlrun/common/schemas/api_gateway.py +196 -0
  27. mlrun/common/schemas/artifact.py +28 -1
  28. mlrun/common/schemas/auth.py +13 -2
  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 +233 -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 +387 -119
  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 +245 -20
  75. mlrun/db/factory.py +1 -4
  76. mlrun/db/httpdb.py +909 -231
  77. mlrun/db/nopdb.py +279 -14
  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 +1176 -406
  164. mlrun/render.py +28 -22
  165. mlrun/run.py +208 -181
  166. mlrun/runtimes/__init__.py +76 -11
  167. mlrun/runtimes/base.py +54 -24
  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/__init__.py +1 -0
  178. mlrun/runtimes/nuclio/api_gateway.py +769 -0
  179. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  180. mlrun/runtimes/nuclio/application/application.py +758 -0
  181. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  182. mlrun/runtimes/nuclio/function.py +188 -68
  183. mlrun/runtimes/nuclio/serving.py +57 -60
  184. mlrun/runtimes/pod.py +191 -58
  185. mlrun/runtimes/remotesparkjob.py +11 -8
  186. mlrun/runtimes/sparkjob/spark3job.py +17 -18
  187. mlrun/runtimes/utils.py +40 -73
  188. mlrun/secrets.py +6 -2
  189. mlrun/serving/__init__.py +8 -1
  190. mlrun/serving/remote.py +2 -3
  191. mlrun/serving/routers.py +89 -64
  192. mlrun/serving/server.py +54 -26
  193. mlrun/serving/states.py +187 -56
  194. mlrun/serving/utils.py +19 -11
  195. mlrun/serving/v2_serving.py +136 -63
  196. mlrun/track/tracker.py +2 -1
  197. mlrun/track/trackers/mlflow_tracker.py +5 -0
  198. mlrun/utils/async_http.py +26 -6
  199. mlrun/utils/db.py +18 -0
  200. mlrun/utils/helpers.py +375 -105
  201. mlrun/utils/http.py +2 -2
  202. mlrun/utils/logger.py +75 -9
  203. mlrun/utils/notifications/notification/__init__.py +14 -10
  204. mlrun/utils/notifications/notification/base.py +48 -0
  205. mlrun/utils/notifications/notification/console.py +2 -0
  206. mlrun/utils/notifications/notification/git.py +24 -1
  207. mlrun/utils/notifications/notification/ipython.py +2 -0
  208. mlrun/utils/notifications/notification/slack.py +96 -21
  209. mlrun/utils/notifications/notification/webhook.py +63 -2
  210. mlrun/utils/notifications/notification_pusher.py +146 -16
  211. mlrun/utils/regex.py +9 -0
  212. mlrun/utils/retryer.py +3 -2
  213. mlrun/utils/v3io_clients.py +2 -3
  214. mlrun/utils/version/version.json +2 -2
  215. mlrun-1.7.2.dist-info/METADATA +390 -0
  216. mlrun-1.7.2.dist-info/RECORD +351 -0
  217. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/WHEEL +1 -1
  218. mlrun/feature_store/retrieval/conversion.py +0 -271
  219. mlrun/kfpops.py +0 -868
  220. mlrun/model_monitoring/application.py +0 -310
  221. mlrun/model_monitoring/batch.py +0 -974
  222. mlrun/model_monitoring/controller_handler.py +0 -37
  223. mlrun/model_monitoring/prometheus.py +0 -216
  224. mlrun/model_monitoring/stores/__init__.py +0 -111
  225. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +0 -574
  226. mlrun/model_monitoring/stores/model_endpoint_store.py +0 -145
  227. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  228. mlrun/model_monitoring/stores/models/base.py +0 -84
  229. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  230. mlrun/platforms/other.py +0 -305
  231. mlrun-1.7.0rc4.dist-info/METADATA +0 -269
  232. mlrun-1.7.0rc4.dist-info/RECORD +0 -321
  233. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/LICENSE +0 -0
  234. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/entry_points.txt +0 -0
  235. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/top_level.txt +0 -0
@@ -11,8 +11,8 @@
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
14
 
15
+ import warnings
16
16
  from typing import Union
17
17
 
18
18
  import mlrun.common.schemas.schedule
@@ -55,6 +55,12 @@ class TrackingPolicy(mlrun.model.ModelObj):
55
55
  writer function, which is a real time nuclio functino, will be deployed
56
56
  with the same image. By default, the image is mlrun/mlrun.
57
57
  """
58
+ warnings.warn(
59
+ "The `TrackingPolicy` class is deprecated from version 1.7.0 and is not "
60
+ "used anymore. It will be removed in 1.9.0.",
61
+ FutureWarning,
62
+ )
63
+
58
64
  if isinstance(default_batch_intervals, str):
59
65
  default_batch_intervals = (
60
66
  mlrun.common.schemas.schedule.ScheduleCronTrigger.from_crontab(
@@ -12,29 +12,29 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import datetime
16
15
  import json
17
- from http import HTTPStatus
18
- from typing import Any, NewType
19
-
20
- import pandas as pd
21
- from v3io.dataplane import Client as V3IOClient
22
- from v3io_frames.client import ClientBase as V3IOFramesClient
23
- from v3io_frames.errors import Error as V3IOFramesError
24
- from v3io_frames.frames_pb2 import IGNORE
16
+ from typing import Any, Callable, NewType
25
17
 
26
18
  import mlrun.common.model_monitoring
19
+ import mlrun.common.schemas
20
+ import mlrun.common.schemas.alert as alert_objects
27
21
  import mlrun.model_monitoring
28
- import mlrun.utils.v3io_clients
29
- from mlrun.common.schemas.model_monitoring.constants import ResultStatusApp, WriterEvent
22
+ from mlrun.common.schemas.model_monitoring.constants import (
23
+ EventFieldType,
24
+ HistogramDataDriftApplicationConstants,
25
+ MetricData,
26
+ ResultData,
27
+ ResultKindApp,
28
+ ResultStatusApp,
29
+ WriterEvent,
30
+ WriterEventKind,
31
+ )
30
32
  from mlrun.common.schemas.notification import NotificationKind, NotificationSeverity
33
+ from mlrun.model_monitoring.helpers import get_result_instance_fqn
31
34
  from mlrun.serving.utils import StepToDict
32
35
  from mlrun.utils import logger
33
36
  from mlrun.utils.notifications.notification_pusher import CustomNotificationPusher
34
37
 
35
- _TSDB_BE = "tsdb"
36
- _TSDB_RATE = "1/s"
37
- _TSDB_TABLE = "app-results"
38
38
  _RawEvent = dict[str, Any]
39
39
  _AppResultEvent = NewType("_AppResultEvent", _RawEvent)
40
40
 
@@ -69,20 +69,20 @@ class _Notifier:
69
69
  self._severity = severity
70
70
 
71
71
  def _should_send_event(self) -> bool:
72
- return self._event[WriterEvent.RESULT_STATUS] >= ResultStatusApp.detected
72
+ return self._event[ResultData.RESULT_STATUS] >= ResultStatusApp.detected.value
73
73
 
74
74
  def _generate_message(self) -> str:
75
75
  return f"""\
76
76
  The monitoring app `{self._event[WriterEvent.APPLICATION_NAME]}` \
77
- of kind `{self._event[WriterEvent.RESULT_KIND]}` \
77
+ of kind `{self._event[ResultData.RESULT_KIND]}` \
78
78
  detected a problem in model endpoint ID `{self._event[WriterEvent.ENDPOINT_ID]}` \
79
79
  at time `{self._event[WriterEvent.START_INFER_TIME]}`.
80
80
 
81
81
  Result data:
82
- Name: `{self._event[WriterEvent.RESULT_NAME]}`
83
- Value: `{self._event[WriterEvent.RESULT_VALUE]}`
84
- Status: `{self._event[WriterEvent.RESULT_STATUS]}`
85
- Extra data: `{self._event[WriterEvent.RESULT_EXTRA_DATA]}`\
82
+ Name: `{self._event[ResultData.RESULT_NAME]}`
83
+ Value: `{self._event[ResultData.RESULT_VALUE]}`
84
+ Status: `{self._event[ResultData.RESULT_STATUS]}`
85
+ Extra data: `{self._event[ResultData.RESULT_EXTRA_DATA]}`\
86
86
  """
87
87
 
88
88
  def notify(self) -> None:
@@ -97,140 +97,176 @@ Extra data: `{self._event[WriterEvent.RESULT_EXTRA_DATA]}`\
97
97
 
98
98
  class ModelMonitoringWriter(StepToDict):
99
99
  """
100
- Write monitoring app events to V3IO KV storage
100
+ Write monitoring application results to the target databases
101
101
  """
102
102
 
103
103
  kind = "monitoring_application_stream_pusher"
104
104
 
105
- def __init__(self, project: str) -> None:
105
+ def __init__(
106
+ self,
107
+ project: str,
108
+ secret_provider: Callable = None,
109
+ ) -> None:
106
110
  self.project = project
107
111
  self.name = project # required for the deployment process
108
- self._v3io_container = self.get_v3io_container(self.name)
109
- self._kv_client = self._get_v3io_client().kv
110
- self._tsdb_client = self._get_v3io_frames_client(self._v3io_container)
112
+
111
113
  self._custom_notifier = CustomNotificationPusher(
112
114
  notification_types=[NotificationKind.slack]
113
115
  )
114
- self._create_tsdb_table()
115
- self._kv_schemas = []
116
-
117
- @staticmethod
118
- def get_v3io_container(project_name: str) -> str:
119
- return f"users/pipelines/{project_name}/monitoring-apps"
120
116
 
121
- @staticmethod
122
- def _get_v3io_client() -> V3IOClient:
123
- return mlrun.utils.v3io_clients.get_v3io_client(
124
- endpoint=mlrun.mlconf.v3io_api,
117
+ self._app_result_store = mlrun.model_monitoring.get_store_object(
118
+ project=self.project, secret_provider=secret_provider
125
119
  )
126
-
127
- @staticmethod
128
- def _get_v3io_frames_client(v3io_container: str) -> V3IOFramesClient:
129
- return mlrun.utils.v3io_clients.get_frames_client(
130
- address=mlrun.mlconf.v3io_framesd,
131
- container=v3io_container,
120
+ self._tsdb_connector = mlrun.model_monitoring.get_tsdb_connector(
121
+ project=self.project, secret_provider=secret_provider
132
122
  )
123
+ self._endpoints_records = {}
133
124
 
134
- def _create_tsdb_table(self) -> None:
135
- self._tsdb_client.create(
136
- backend=_TSDB_BE,
137
- table=_TSDB_TABLE,
138
- if_exists=IGNORE,
139
- rate=_TSDB_RATE,
125
+ def _generate_event_on_drift(
126
+ self,
127
+ entity_id: str,
128
+ result_status: int,
129
+ event_value: dict,
130
+ project_name: str,
131
+ result_kind: int,
132
+ ) -> None:
133
+ entity = mlrun.common.schemas.alert.EventEntities(
134
+ kind=alert_objects.EventEntityKind.MODEL_ENDPOINT_RESULT,
135
+ project=project_name,
136
+ ids=[entity_id],
140
137
  )
141
138
 
142
- def _update_kv_db(self, event: _AppResultEvent) -> None:
143
- event = _AppResultEvent(event.copy())
144
- endpoint_id = event.pop(WriterEvent.ENDPOINT_ID)
145
- app_name = event.pop(WriterEvent.APPLICATION_NAME)
146
- metric_name = event.pop(WriterEvent.RESULT_NAME)
147
- attributes = {metric_name: json.dumps(event)}
148
- self._kv_client.update(
149
- container=self._v3io_container,
150
- table_path=endpoint_id,
151
- key=app_name,
152
- attributes=attributes,
139
+ event_kind = self._generate_alert_event_kind(
140
+ result_status=result_status, result_kind=result_kind
153
141
  )
154
- if endpoint_id not in self._kv_schemas:
155
- self._generate_kv_schema(endpoint_id)
156
- logger.info("Updated V3IO KV successfully", key=app_name)
157
-
158
- def _generate_kv_schema(self, endpoint_id: str):
159
- """Generate V3IO KV schema file which will be used by the model monitoring applications dashboard in Grafana."""
160
- fields = [
161
- {"name": WriterEvent.RESULT_NAME, "type": "string", "nullable": False}
162
- ]
163
- res = self._kv_client.create_schema(
164
- container=self._v3io_container,
165
- table_path=endpoint_id,
166
- key=WriterEvent.APPLICATION_NAME,
167
- fields=fields,
142
+
143
+ event_data = mlrun.common.schemas.Event(
144
+ kind=alert_objects.EventKind(value=event_kind),
145
+ entity=entity,
146
+ value_dict=event_value,
168
147
  )
169
- if res.status_code != HTTPStatus.OK.value:
170
- raise mlrun.errors.MLRunBadRequestError(
171
- f"Couldn't infer schema for endpoint {endpoint_id} which is required for Grafana dashboards"
172
- )
173
- else:
174
- logger.info(
175
- "Generated V3IO KV schema successfully", endpoint_id=endpoint_id
176
- )
177
- self._kv_schemas.append(endpoint_id)
148
+ logger.info("Sending a drift event")
149
+ mlrun.get_run_db().generate_event(event_kind, event_data)
150
+ logger.info("Drift event sent successfully")
178
151
 
179
- def _update_tsdb(self, event: _AppResultEvent) -> None:
180
- event = _AppResultEvent(event.copy())
181
- event[WriterEvent.END_INFER_TIME] = datetime.datetime.fromisoformat(
182
- event[WriterEvent.END_INFER_TIME]
152
+ @staticmethod
153
+ def _generate_alert_event_kind(
154
+ result_kind: int, result_status: int
155
+ ) -> alert_objects.EventKind:
156
+ """Generate the required Event Kind format for the alerting system"""
157
+ event_kind = ResultKindApp(value=result_kind).name
158
+
159
+ if result_status == ResultStatusApp.detected.value:
160
+ event_kind = f"{event_kind}_detected"
161
+ else:
162
+ event_kind = f"{event_kind}_suspected"
163
+ return alert_objects.EventKind(
164
+ value=mlrun.utils.helpers.normalize_name(event_kind)
183
165
  )
184
- del event[WriterEvent.RESULT_EXTRA_DATA]
185
- try:
186
- self._tsdb_client.write(
187
- backend=_TSDB_BE,
188
- table=_TSDB_TABLE,
189
- dfs=pd.DataFrame.from_records([event]),
190
- index_cols=[
191
- WriterEvent.END_INFER_TIME,
192
- WriterEvent.ENDPOINT_ID,
193
- WriterEvent.APPLICATION_NAME,
194
- WriterEvent.RESULT_NAME,
195
- ],
196
- )
197
- logger.info("Updated V3IO TSDB successfully", table=_TSDB_TABLE)
198
- except V3IOFramesError as err:
199
- logger.warn(
200
- "Could not write drift measures to TSDB",
201
- err=err,
202
- table=_TSDB_TABLE,
203
- event=event,
204
- )
205
166
 
206
167
  @staticmethod
207
- def _reconstruct_event(event: _RawEvent) -> _AppResultEvent:
168
+ def _reconstruct_event(event: _RawEvent) -> tuple[_AppResultEvent, WriterEventKind]:
208
169
  """
209
170
  Modify the raw event into the expected monitoring application event
210
171
  schema as defined in `mlrun.common.schemas.model_monitoring.constants.WriterEvent`
211
172
  """
212
- try:
213
- result_event = _AppResultEvent(
214
- {key: event[key] for key in WriterEvent.list()}
173
+ if not isinstance(event, dict):
174
+ raise _WriterEventTypeError(
175
+ f"The event is of type: {type(event)}, expected a dictionary"
215
176
  )
216
- result_event[WriterEvent.CURRENT_STATS] = json.loads(
217
- event[WriterEvent.CURRENT_STATS]
177
+ kind = event.pop(WriterEvent.EVENT_KIND, WriterEventKind.RESULT)
178
+ result_event = _AppResultEvent(json.loads(event.pop(WriterEvent.DATA, "{}")))
179
+ if not result_event: # BC for < 1.7.0, can be removed in 1.9.0
180
+ result_event = _AppResultEvent(event)
181
+ else:
182
+ result_event.update(_AppResultEvent(event))
183
+
184
+ expected_keys = list(
185
+ set(WriterEvent.list()).difference(
186
+ [WriterEvent.EVENT_KIND, WriterEvent.DATA]
218
187
  )
219
- return result_event
220
- except KeyError as err:
188
+ )
189
+ if kind == WriterEventKind.METRIC:
190
+ expected_keys.extend(MetricData.list())
191
+ elif kind == WriterEventKind.RESULT:
192
+ expected_keys.extend(ResultData.list())
193
+ else:
221
194
  raise _WriterEventValueError(
222
- "The received event misses some keys compared to the expected "
223
- "monitoring application event schema"
224
- ) from err
225
- except TypeError as err:
226
- raise _WriterEventTypeError(
227
- f"The event is of type: {type(event)}, expected a dictionary"
228
- ) from err
195
+ f"Unknown event kind: {kind}, expected one of: {WriterEventKind.list()}"
196
+ )
197
+ missing_keys = [key for key in expected_keys if key not in result_event]
198
+ if missing_keys:
199
+ raise _WriterEventValueError(
200
+ f"The received event misses some keys compared to the expected "
201
+ f"monitoring application event schema: {missing_keys}"
202
+ )
203
+
204
+ return result_event, kind
229
205
 
230
206
  def do(self, event: _RawEvent) -> None:
231
- event = self._reconstruct_event(event)
207
+ event, kind = self._reconstruct_event(event)
232
208
  logger.info("Starting to write event", event=event)
233
- self._update_tsdb(event)
234
- self._update_kv_db(event)
235
- _Notifier(event=event, notification_pusher=self._custom_notifier).notify()
209
+ self._tsdb_connector.write_application_event(event=event.copy(), kind=kind)
210
+ self._app_result_store.write_application_event(event=event.copy(), kind=kind)
211
+
236
212
  logger.info("Completed event DB writes")
213
+
214
+ if kind == WriterEventKind.RESULT:
215
+ _Notifier(event=event, notification_pusher=self._custom_notifier).notify()
216
+
217
+ if (
218
+ mlrun.mlconf.alerts.mode == mlrun.common.schemas.alert.AlertsModes.enabled
219
+ and kind == WriterEventKind.RESULT
220
+ and (
221
+ event[ResultData.RESULT_STATUS] == ResultStatusApp.detected.value
222
+ or event[ResultData.RESULT_STATUS]
223
+ == ResultStatusApp.potential_detection.value
224
+ )
225
+ ):
226
+ endpoint_id = event[WriterEvent.ENDPOINT_ID]
227
+ endpoint_record = self._endpoints_records.setdefault(
228
+ endpoint_id,
229
+ self._app_result_store.get_model_endpoint(endpoint_id=endpoint_id),
230
+ )
231
+ event_value = {
232
+ "app_name": event[WriterEvent.APPLICATION_NAME],
233
+ "model": endpoint_record.get(EventFieldType.MODEL),
234
+ "model_endpoint_id": event[WriterEvent.ENDPOINT_ID],
235
+ "result_name": event[ResultData.RESULT_NAME],
236
+ "result_value": event[ResultData.RESULT_VALUE],
237
+ }
238
+ self._generate_event_on_drift(
239
+ entity_id=get_result_instance_fqn(
240
+ event[WriterEvent.ENDPOINT_ID],
241
+ event[WriterEvent.APPLICATION_NAME],
242
+ event[ResultData.RESULT_NAME],
243
+ ),
244
+ result_status=event[ResultData.RESULT_STATUS],
245
+ event_value=event_value,
246
+ project_name=self.project,
247
+ result_kind=event[ResultData.RESULT_KIND],
248
+ )
249
+
250
+ if (
251
+ kind == WriterEventKind.RESULT
252
+ and event[WriterEvent.APPLICATION_NAME]
253
+ == HistogramDataDriftApplicationConstants.NAME
254
+ and event[ResultData.RESULT_NAME]
255
+ == HistogramDataDriftApplicationConstants.GENERAL_RESULT_NAME
256
+ ):
257
+ endpoint_id = event[WriterEvent.ENDPOINT_ID]
258
+ logger.info(
259
+ "Updating the model endpoint with metadata specific to the histogram "
260
+ "data drift app",
261
+ endpoint_id=endpoint_id,
262
+ )
263
+ attributes = json.loads(event[ResultData.RESULT_EXTRA_DATA])
264
+ attributes[EventFieldType.DRIFT_STATUS] = str(
265
+ attributes[EventFieldType.DRIFT_STATUS]
266
+ )
267
+ self._app_result_store.update_model_endpoint(
268
+ endpoint_id=endpoint_id,
269
+ attributes=attributes,
270
+ )
271
+
272
+ logger.info("Model monitoring writer finished handling event")
@@ -34,7 +34,7 @@ class _DefaultPackagerMeta(ABCMeta):
34
34
  dynamically generated docstring that will include a summary of the packager.
35
35
  """
36
36
 
37
- def __new__(mcls, name: str, bases: tuple, namespace: dict, **kwargs):
37
+ def __new__(cls, name: str, bases: tuple, namespace: dict, **kwargs):
38
38
  """
39
39
  Create a new DefaultPackager metaclass that saves the original packager docstring to another attribute named
40
40
  `_packager_doc`.
@@ -48,7 +48,7 @@ class _DefaultPackagerMeta(ABCMeta):
48
48
  namespace["_packager_doc"] = namespace.get("__doc__", "")
49
49
 
50
50
  # Continue creating the metaclass:
51
- return super().__new__(mcls, name, bases, namespace, **kwargs)
51
+ return super().__new__(cls, name, bases, namespace, **kwargs)
52
52
 
53
53
  @property
54
54
  def __doc__(cls: type["DefaultPackager"]) -> str:
@@ -92,6 +92,7 @@ class PackagersManager:
92
92
  from mlrun import Packager
93
93
  from x import XPackager
94
94
 
95
+
95
96
  class YPackager(Packager):
96
97
  pass
97
98
 
@@ -142,11 +142,11 @@ class _YAMLFormatter(_Formatter):
142
142
 
143
143
  :param obj: The object to write.
144
144
  :param file_path: The file path to write to.
145
- :param dump_kwargs: Additional keyword arguments to pass to the `yaml.dump` method of the formatter in use.
145
+ :param dump_kwargs: Additional keyword arguments to pass to the `yaml.safe_dump` method of the formatter in use.
146
146
  """
147
147
  dump_kwargs = dump_kwargs or cls.DEFAULT_DUMP_KWARGS
148
148
  with open(file_path, "w") as file:
149
- yaml.dump(obj, file, **dump_kwargs)
149
+ yaml.safe_dump(obj, file, **dump_kwargs)
150
150
 
151
151
  @classmethod
152
152
  def read(cls, file_path: str) -> Union[list, dict]:
@@ -17,22 +17,23 @@ import json
17
17
  from pprint import pprint
18
18
  from time import sleep
19
19
 
20
- from .iguazio import (
21
- V3ioStreamClient,
22
- VolumeMount,
23
- add_or_refresh_credentials,
24
- is_iguazio_session_cookie,
25
- mount_v3io,
26
- v3io_cred,
27
- )
28
- from .other import (
20
+ from mlrun_pipelines.common.mounts import VolumeMount
21
+ from mlrun_pipelines.mounts import (
29
22
  auto_mount,
30
23
  mount_configmap,
31
24
  mount_hostpath,
32
25
  mount_pvc,
33
26
  mount_s3,
34
27
  mount_secret,
28
+ mount_v3io,
35
29
  set_env_variables,
30
+ v3io_cred,
31
+ )
32
+
33
+ from .iguazio import (
34
+ V3ioStreamClient,
35
+ add_or_refresh_credentials,
36
+ is_iguazio_session_cookie,
36
37
  )
37
38
 
38
39
 
@@ -48,7 +49,7 @@ def watch_stream(
48
49
 
49
50
  example::
50
51
 
51
- watch_stream('v3io:///users/admin/mystream')
52
+ watch_stream("v3io:///users/admin/mystream")
52
53
 
53
54
  :param url: stream url
54
55
  :param shard_ids: range or list of shard IDs