mlrun 1.7.0rc28__py3-none-any.whl → 1.7.0rc55__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 (135) hide show
  1. mlrun/__main__.py +4 -2
  2. mlrun/alerts/alert.py +75 -8
  3. mlrun/artifacts/base.py +1 -0
  4. mlrun/artifacts/manager.py +9 -2
  5. mlrun/common/constants.py +4 -1
  6. mlrun/common/db/sql_session.py +3 -2
  7. mlrun/common/formatters/__init__.py +1 -0
  8. mlrun/common/formatters/artifact.py +1 -0
  9. mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
  10. mlrun/common/formatters/run.py +3 -0
  11. mlrun/common/helpers.py +0 -1
  12. mlrun/common/schemas/__init__.py +3 -1
  13. mlrun/common/schemas/alert.py +15 -12
  14. mlrun/common/schemas/api_gateway.py +6 -6
  15. mlrun/common/schemas/auth.py +5 -0
  16. mlrun/common/schemas/client_spec.py +0 -1
  17. mlrun/common/schemas/common.py +7 -4
  18. mlrun/common/schemas/frontend_spec.py +7 -0
  19. mlrun/common/schemas/function.py +7 -0
  20. mlrun/common/schemas/model_monitoring/__init__.py +4 -3
  21. mlrun/common/schemas/model_monitoring/constants.py +41 -26
  22. mlrun/common/schemas/model_monitoring/model_endpoints.py +23 -47
  23. mlrun/common/schemas/notification.py +69 -12
  24. mlrun/common/schemas/project.py +45 -12
  25. mlrun/common/schemas/workflow.py +10 -2
  26. mlrun/common/types.py +1 -0
  27. mlrun/config.py +91 -35
  28. mlrun/data_types/data_types.py +6 -1
  29. mlrun/data_types/spark.py +2 -2
  30. mlrun/data_types/to_pandas.py +57 -25
  31. mlrun/datastore/__init__.py +1 -0
  32. mlrun/datastore/alibaba_oss.py +3 -2
  33. mlrun/datastore/azure_blob.py +125 -37
  34. mlrun/datastore/base.py +42 -21
  35. mlrun/datastore/datastore.py +4 -2
  36. mlrun/datastore/datastore_profile.py +1 -1
  37. mlrun/datastore/dbfs_store.py +3 -7
  38. mlrun/datastore/filestore.py +1 -3
  39. mlrun/datastore/google_cloud_storage.py +85 -29
  40. mlrun/datastore/inmem.py +4 -1
  41. mlrun/datastore/redis.py +1 -0
  42. mlrun/datastore/s3.py +25 -12
  43. mlrun/datastore/sources.py +76 -4
  44. mlrun/datastore/spark_utils.py +30 -0
  45. mlrun/datastore/storeytargets.py +151 -0
  46. mlrun/datastore/targets.py +102 -131
  47. mlrun/datastore/v3io.py +1 -0
  48. mlrun/db/base.py +15 -6
  49. mlrun/db/httpdb.py +57 -28
  50. mlrun/db/nopdb.py +29 -5
  51. mlrun/errors.py +20 -3
  52. mlrun/execution.py +46 -5
  53. mlrun/feature_store/api.py +25 -1
  54. mlrun/feature_store/common.py +6 -11
  55. mlrun/feature_store/feature_vector.py +3 -1
  56. mlrun/feature_store/retrieval/job.py +4 -1
  57. mlrun/feature_store/retrieval/spark_merger.py +10 -39
  58. mlrun/feature_store/steps.py +8 -0
  59. mlrun/frameworks/_common/plan.py +3 -3
  60. mlrun/frameworks/_ml_common/plan.py +1 -1
  61. mlrun/frameworks/parallel_coordinates.py +2 -3
  62. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  63. mlrun/k8s_utils.py +48 -2
  64. mlrun/launcher/client.py +6 -6
  65. mlrun/launcher/local.py +2 -2
  66. mlrun/model.py +215 -34
  67. mlrun/model_monitoring/api.py +38 -24
  68. mlrun/model_monitoring/applications/__init__.py +1 -2
  69. mlrun/model_monitoring/applications/_application_steps.py +60 -29
  70. mlrun/model_monitoring/applications/base.py +2 -174
  71. mlrun/model_monitoring/applications/context.py +197 -70
  72. mlrun/model_monitoring/applications/evidently_base.py +11 -85
  73. mlrun/model_monitoring/applications/histogram_data_drift.py +21 -16
  74. mlrun/model_monitoring/applications/results.py +4 -4
  75. mlrun/model_monitoring/controller.py +110 -282
  76. mlrun/model_monitoring/db/stores/__init__.py +8 -3
  77. mlrun/model_monitoring/db/stores/base/store.py +3 -0
  78. mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
  79. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +18 -3
  80. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +43 -23
  81. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +48 -35
  82. mlrun/model_monitoring/db/tsdb/__init__.py +7 -2
  83. mlrun/model_monitoring/db/tsdb/base.py +147 -15
  84. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +94 -55
  85. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -3
  86. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +144 -38
  87. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +44 -3
  88. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +246 -57
  89. mlrun/model_monitoring/helpers.py +70 -50
  90. mlrun/model_monitoring/stream_processing.py +96 -195
  91. mlrun/model_monitoring/writer.py +13 -5
  92. mlrun/package/packagers/default_packager.py +2 -2
  93. mlrun/projects/operations.py +16 -8
  94. mlrun/projects/pipelines.py +126 -115
  95. mlrun/projects/project.py +286 -129
  96. mlrun/render.py +3 -3
  97. mlrun/run.py +38 -19
  98. mlrun/runtimes/__init__.py +19 -8
  99. mlrun/runtimes/base.py +4 -1
  100. mlrun/runtimes/daskjob.py +1 -1
  101. mlrun/runtimes/funcdoc.py +1 -1
  102. mlrun/runtimes/kubejob.py +6 -6
  103. mlrun/runtimes/local.py +12 -5
  104. mlrun/runtimes/nuclio/api_gateway.py +68 -8
  105. mlrun/runtimes/nuclio/application/application.py +307 -70
  106. mlrun/runtimes/nuclio/function.py +63 -14
  107. mlrun/runtimes/nuclio/serving.py +10 -10
  108. mlrun/runtimes/pod.py +25 -19
  109. mlrun/runtimes/remotesparkjob.py +2 -5
  110. mlrun/runtimes/sparkjob/spark3job.py +16 -17
  111. mlrun/runtimes/utils.py +34 -0
  112. mlrun/serving/routers.py +2 -5
  113. mlrun/serving/server.py +37 -19
  114. mlrun/serving/states.py +30 -3
  115. mlrun/serving/v2_serving.py +44 -35
  116. mlrun/track/trackers/mlflow_tracker.py +5 -0
  117. mlrun/utils/async_http.py +1 -1
  118. mlrun/utils/db.py +18 -0
  119. mlrun/utils/helpers.py +150 -36
  120. mlrun/utils/http.py +1 -1
  121. mlrun/utils/notifications/notification/__init__.py +0 -1
  122. mlrun/utils/notifications/notification/webhook.py +8 -1
  123. mlrun/utils/notifications/notification_pusher.py +1 -1
  124. mlrun/utils/v3io_clients.py +2 -2
  125. mlrun/utils/version/version.json +2 -2
  126. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +153 -66
  127. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +131 -134
  128. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
  129. mlrun/feature_store/retrieval/conversion.py +0 -271
  130. mlrun/model_monitoring/controller_handler.py +0 -37
  131. mlrun/model_monitoring/evidently_application.py +0 -20
  132. mlrun/model_monitoring/prometheus.py +0 -216
  133. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
  134. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
  135. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
@@ -24,7 +24,6 @@ import mlrun.artifacts
24
24
  import mlrun.common.helpers
25
25
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
26
26
  import mlrun.feature_store
27
- import mlrun.model_monitoring.application
28
27
  import mlrun.model_monitoring.applications as mm_app
29
28
  import mlrun.serving
30
29
  from mlrun.data_types.infer import InferOptions, get_df_stats
@@ -147,8 +146,7 @@ def record_results(
147
146
  on the provided `endpoint_id`.
148
147
  :param function_name: If a new model endpoint is created, use this function name for generating the
149
148
  function URI.
150
- :param context: MLRun context. Note that the context is required for logging the artifacts
151
- following the batch drift job.
149
+ :param context: MLRun context. Note that the context is required generating the model endpoint.
152
150
  :param infer_results_df: DataFrame that will be stored under the model endpoint parquet target. Will be
153
151
  used for doing the drift analysis. Please make sure that the dataframe includes
154
152
  both feature names and label columns.
@@ -252,14 +250,31 @@ def _model_endpoint_validations(
252
250
  In case of discrepancy between the provided `sample_set_statistics` and the
253
251
  `model_endpoints.spec.feature_stats`, a warning will be presented to the user.
254
252
  """
255
- # Model path
256
- if model_path and model_endpoint.spec.model_uri != model_path:
257
- raise mlrun.errors.MLRunInvalidArgumentError(
258
- f"provided model store path {model_path} does not match "
259
- f"the path that is stored under the existing model "
260
- 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,
264
+ )
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
261
269
  )
262
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
+
263
278
  # Feature stats
264
279
  if (
265
280
  sample_set_statistics
@@ -545,8 +560,7 @@ def _create_model_monitoring_function_base(
545
560
  func: typing.Union[str, None] = None,
546
561
  application_class: typing.Union[
547
562
  str,
548
- mlrun.model_monitoring.application.ModelMonitoringApplicationBase,
549
- mm_app.ModelMonitoringApplicationBaseV2,
563
+ mm_app.ModelMonitoringApplicationBase,
550
564
  None,
551
565
  ] = None,
552
566
  name: typing.Optional[str] = None,
@@ -560,19 +574,10 @@ def _create_model_monitoring_function_base(
560
574
  Note: this is an internal API only.
561
575
  This function does not set the labels or mounts v3io.
562
576
  """
563
- if isinstance(
564
- application_class,
565
- mlrun.model_monitoring.application.ModelMonitoringApplicationBase,
566
- ):
567
- warnings.warn(
568
- "The `ModelMonitoringApplicationBase` class is deprecated from version 1.7.0, "
569
- "please use `ModelMonitoringApplicationBaseV2`. It will be removed in 1.9.0.",
570
- FutureWarning,
571
- )
572
- if name in mm_constants.MonitoringFunctionNames.list():
577
+ if name in mm_constants._RESERVED_FUNCTION_NAMES:
573
578
  raise mlrun.errors.MLRunInvalidArgumentError(
574
- f"An application cannot have the following names: "
575
- f"{mm_constants.MonitoringFunctionNames.list()}"
579
+ "An application cannot have the following names: "
580
+ f"{mm_constants._RESERVED_FUNCTION_NAMES}"
576
581
  )
577
582
  if func is None:
578
583
  func = ""
@@ -599,11 +604,20 @@ def _create_model_monitoring_function_base(
599
604
  app_step = prepare_step.to(class_name=application_class, **application_kwargs)
600
605
  else:
601
606
  app_step = prepare_step.to(class_name=application_class)
607
+
602
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
+
603
617
  app_step.to(
604
618
  class_name="mlrun.model_monitoring.applications._application_steps._PushToMonitoringWriter",
605
619
  name="PushToMonitoringWriter",
606
620
  project=project,
607
621
  writer_application_name=mm_constants.MonitoringFunctionNames.WRITER,
608
- ).respond()
622
+ )
609
623
  return func_obj
@@ -13,12 +13,11 @@
13
13
  # limitations under the License.
14
14
  #
15
15
 
16
- from .base import ModelMonitoringApplicationBase, ModelMonitoringApplicationBaseV2
16
+ from .base import ModelMonitoringApplicationBase
17
17
  from .context import MonitoringApplicationContext
18
18
  from .evidently_base import (
19
19
  _HAS_EVIDENTLY,
20
20
  SUPPORTED_EVIDENTLY_VERSION,
21
21
  EvidentlyModelMonitoringApplicationBase,
22
- EvidentlyModelMonitoringApplicationBaseV2,
23
22
  )
24
23
  from .results import ModelMonitoringApplicationMetric, ModelMonitoringApplicationResult
@@ -11,16 +11,17 @@
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
+
14
15
  import json
15
- import typing
16
- from typing import Optional
16
+ import traceback
17
+ from typing import Any, Optional, Union
17
18
 
18
- import mlrun.common.helpers
19
- import mlrun.common.model_monitoring.helpers
19
+ import mlrun.common.schemas.alert as alert_objects
20
20
  import mlrun.common.schemas.model_monitoring.constants as mm_constant
21
21
  import mlrun.datastore
22
- import mlrun.utils.v3io_clients
22
+ import mlrun.model_monitoring
23
23
  from mlrun.model_monitoring.helpers import get_stream_path
24
+ from mlrun.serving import GraphContext
24
25
  from mlrun.serving.utils import StepToDict
25
26
  from mlrun.utils import logger
26
27
 
@@ -33,8 +34,8 @@ class _PushToMonitoringWriter(StepToDict):
33
34
 
34
35
  def __init__(
35
36
  self,
36
- project: Optional[str] = None,
37
- writer_application_name: Optional[str] = None,
37
+ project: str,
38
+ writer_application_name: str,
38
39
  stream_uri: Optional[str] = None,
39
40
  name: Optional[str] = None,
40
41
  ):
@@ -59,7 +60,7 @@ class _PushToMonitoringWriter(StepToDict):
59
60
  self,
60
61
  event: tuple[
61
62
  list[
62
- typing.Union[
63
+ Union[
63
64
  ModelMonitoringApplicationResult, ModelMonitoringApplicationMetric
64
65
  ]
65
66
  ],
@@ -108,6 +109,7 @@ class _PushToMonitoringWriter(StepToDict):
108
109
  f"Pushing data = {writer_event} \n to stream = {self.stream_uri}"
109
110
  )
110
111
  self.output_stream.push([writer_event])
112
+ logger.info(f"Pushed data to {self.stream_uri} successfully")
111
113
 
112
114
  def _lazy_init(self):
113
115
  if self.output_stream is None:
@@ -117,41 +119,70 @@ class _PushToMonitoringWriter(StepToDict):
117
119
 
118
120
 
119
121
  class _PrepareMonitoringEvent(StepToDict):
120
- def __init__(self, application_name: str):
122
+ def __init__(self, context: GraphContext, application_name: str) -> None:
121
123
  """
122
124
  Class for preparing the application event for the application step.
123
125
 
124
126
  :param application_name: Application name.
125
127
  """
128
+ self.graph_context = context
129
+ self.application_name = application_name
130
+ self.model_endpoints: dict[str, mlrun.model_monitoring.ModelEndpoint] = {}
126
131
 
127
- self.context = self._create_mlrun_context(application_name)
128
- self.model_endpoints = {}
129
-
130
- def do(self, event: dict[str, dict]) -> MonitoringApplicationContext:
132
+ def do(self, event: dict[str, Any]) -> MonitoringApplicationContext:
131
133
  """
132
134
  Prepare the application event for the application step.
133
135
 
134
136
  :param event: Application event.
135
- :return: Application event.
137
+ :return: Application context.
136
138
  """
137
- if not event.get("mlrun_context"):
138
- application_context = MonitoringApplicationContext().from_dict(
139
- event,
140
- context=self.context,
141
- model_endpoint_dict=self.model_endpoints,
142
- )
143
- else:
144
- application_context = MonitoringApplicationContext().from_dict(event)
139
+ application_context = MonitoringApplicationContext(
140
+ graph_context=self.graph_context,
141
+ application_name=self.application_name,
142
+ event=event,
143
+ model_endpoint_dict=self.model_endpoints,
144
+ )
145
+
145
146
  self.model_endpoints.setdefault(
146
147
  application_context.endpoint_id, application_context.model_endpoint
147
148
  )
149
+
148
150
  return application_context
149
151
 
150
- @staticmethod
151
- def _create_mlrun_context(app_name: str):
152
- context = mlrun.get_or_create_ctx(
153
- f"{app_name}-logger",
154
- upload_artifacts=True,
152
+
153
+ class _ApplicationErrorHandler(StepToDict):
154
+ def __init__(self, project: str, name: Optional[str] = None):
155
+ self.project = project
156
+ self.name = name or "ApplicationErrorHandler"
157
+
158
+ def do(self, event):
159
+ """
160
+ Handle model monitoring application error. This step will generate an event, describing the error.
161
+
162
+ :param event: Application event.
163
+ """
164
+
165
+ exception_with_trace = "".join(
166
+ traceback.format_exception(None, event.error, event.error.__traceback__)
167
+ )
168
+ logger.error(f"Error in application step: {exception_with_trace}")
169
+
170
+ event_data = alert_objects.Event(
171
+ kind=alert_objects.EventKind.MM_APP_FAILED,
172
+ entity=alert_objects.EventEntities(
173
+ kind=alert_objects.EventEntityKind.MODEL_MONITORING_APPLICATION,
174
+ project=self.project,
175
+ ids=[f"{self.project}_{event.body.application_name}"],
176
+ ),
177
+ value_dict={
178
+ "Error": event.error,
179
+ "Timestamp": event.timestamp,
180
+ "Application Class": event.body.application_name,
181
+ "Endpoint ID": event.body.endpoint_id,
182
+ },
183
+ )
184
+
185
+ mlrun.get_run_db().generate_event(
186
+ name=alert_objects.EventKind.MM_APP_FAILED, event_data=event_data
155
187
  )
156
- context.__class__ = MonitoringApplicationContext
157
- return context
188
+ logger.info("Event generated successfully")
@@ -13,18 +13,14 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from abc import ABC, abstractmethod
16
- from typing import Any, Union, cast
16
+ from typing import Any, Union
17
17
 
18
- import numpy as np
19
- import pandas as pd
20
-
21
- import mlrun
22
18
  import mlrun.model_monitoring.applications.context as mm_context
23
19
  import mlrun.model_monitoring.applications.results as mm_results
24
20
  from mlrun.serving.utils import MonitoringApplicationToDict
25
21
 
26
22
 
27
- class ModelMonitoringApplicationBaseV2(MonitoringApplicationToDict, ABC):
23
+ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
28
24
  """
29
25
  A base class for a model monitoring application.
30
26
  Inherit from this class to create a custom model monitoring application.
@@ -110,171 +106,3 @@ class ModelMonitoringApplicationBaseV2(MonitoringApplicationToDict, ABC):
110
106
  each metric name is the key and the metric value is the corresponding value).
111
107
  """
112
108
  raise NotImplementedError
113
-
114
-
115
- class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
116
- """
117
- A base class for a model monitoring application.
118
- Inherit from this class to create a custom model monitoring application.
119
-
120
- example for very simple custom application::
121
-
122
- class MyApp(ApplicationBase):
123
- def do_tracking(
124
- self,
125
- sample_df_stats: mlrun.common.model_monitoring.helpers.FeatureStats,
126
- feature_stats: mlrun.common.model_monitoring.helpers.FeatureStats,
127
- start_infer_time: pd.Timestamp,
128
- end_infer_time: pd.Timestamp,
129
- schedule_time: pd.Timestamp,
130
- latest_request: pd.Timestamp,
131
- endpoint_id: str,
132
- output_stream_uri: str,
133
- ) -> ModelMonitoringApplicationResult:
134
- self.context.log_artifact(
135
- TableArtifact(
136
- "sample_df_stats", df=self.dict_to_histogram(sample_df_stats)
137
- )
138
- )
139
- return ModelMonitoringApplicationResult(
140
- name="data_drift_test",
141
- value=0.5,
142
- kind=mm_constant.ResultKindApp.data_drift,
143
- status=mm_constant.ResultStatusApp.detected,
144
- )
145
-
146
-
147
- """
148
-
149
- kind = "monitoring_application"
150
-
151
- def do(
152
- self, monitoring_context: mm_context.MonitoringApplicationContext
153
- ) -> tuple[
154
- list[mm_results.ModelMonitoringApplicationResult],
155
- mm_context.MonitoringApplicationContext,
156
- ]:
157
- """
158
- Process the monitoring event and return application results.
159
-
160
- :param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
161
- :returns: A tuple of:
162
- [0] = list of application results that can be either from type
163
- `ModelMonitoringApplicationResult` or from type
164
- `ModelMonitoringApplicationResult`.
165
- [1] = the original application event, wrapped in `MonitoringApplicationContext`
166
- object
167
- """
168
- resolved_event = self._resolve_event(monitoring_context)
169
- if not (
170
- hasattr(self, "context") and isinstance(self.context, mlrun.MLClientCtx)
171
- ):
172
- self._lazy_init(monitoring_context)
173
- results = self.do_tracking(*resolved_event)
174
- results = results if isinstance(results, list) else [results]
175
- return results, monitoring_context
176
-
177
- def _lazy_init(self, monitoring_context: mm_context.MonitoringApplicationContext):
178
- self.context = cast(mlrun.MLClientCtx, monitoring_context)
179
-
180
- @abstractmethod
181
- def do_tracking(
182
- self,
183
- application_name: str,
184
- sample_df_stats: pd.DataFrame,
185
- feature_stats: pd.DataFrame,
186
- sample_df: pd.DataFrame,
187
- start_infer_time: pd.Timestamp,
188
- end_infer_time: pd.Timestamp,
189
- latest_request: pd.Timestamp,
190
- endpoint_id: str,
191
- output_stream_uri: str,
192
- ) -> Union[
193
- mm_results.ModelMonitoringApplicationResult,
194
- list[mm_results.ModelMonitoringApplicationResult],
195
- ]:
196
- """
197
- Implement this method with your custom monitoring logic.
198
-
199
- :param application_name: (str) the app name
200
- :param sample_df_stats: (pd.DataFrame) The new sample distribution.
201
- :param feature_stats: (pd.DataFrame) The train sample distribution.
202
- :param sample_df: (pd.DataFrame) The new sample DataFrame.
203
- :param start_infer_time: (pd.Timestamp) Start time of the monitoring schedule.
204
- :param end_infer_time: (pd.Timestamp) End time of the monitoring schedule.
205
- :param latest_request: (pd.Timestamp) Timestamp of the latest request on this endpoint_id.
206
- :param endpoint_id: (str) ID of the monitored model endpoint
207
- :param output_stream_uri: (str) URI of the output stream for results
208
-
209
- :returns: (ModelMonitoringApplicationResult) or
210
- (list[ModelMonitoringApplicationResult]) of the application results.
211
- """
212
- raise NotImplementedError
213
-
214
- @classmethod
215
- def _resolve_event(
216
- cls,
217
- monitoring_context: mm_context.MonitoringApplicationContext,
218
- ) -> tuple[
219
- str,
220
- pd.DataFrame,
221
- pd.DataFrame,
222
- pd.DataFrame,
223
- pd.Timestamp,
224
- pd.Timestamp,
225
- pd.Timestamp,
226
- str,
227
- str,
228
- ]:
229
- """
230
- Converting the event into a single tuple that will be used for passing the event arguments to the running
231
- application
232
-
233
- :param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
234
-
235
- :return: A tuple of:
236
- [0] = (str) application name
237
- [1] = (pd.DataFrame) current input statistics
238
- [2] = (pd.DataFrame) train statistics
239
- [3] = (pd.DataFrame) current input data
240
- [4] = (pd.Timestamp) start time of the monitoring schedule
241
- [5] = (pd.Timestamp) end time of the monitoring schedule
242
- [6] = (pd.Timestamp) timestamp of the latest request
243
- [7] = (str) endpoint id
244
- [8] = (str) output stream uri
245
- """
246
- return (
247
- monitoring_context.application_name,
248
- cls.dict_to_histogram(monitoring_context.sample_df_stats),
249
- cls.dict_to_histogram(monitoring_context.feature_stats),
250
- monitoring_context.sample_df,
251
- monitoring_context.start_infer_time,
252
- monitoring_context.end_infer_time,
253
- monitoring_context.latest_request,
254
- monitoring_context.endpoint_id,
255
- monitoring_context.output_stream_uri,
256
- )
257
-
258
- @staticmethod
259
- def dict_to_histogram(
260
- histogram_dict: mlrun.common.model_monitoring.helpers.FeatureStats,
261
- ) -> pd.DataFrame:
262
- """
263
- Convert histogram dictionary to pandas DataFrame with feature histograms as columns
264
-
265
- :param histogram_dict: Histogram dictionary
266
-
267
- :returns: Histogram dataframe
268
- """
269
-
270
- # Create a dictionary with feature histograms as values
271
- histograms = {}
272
- for feature, stats in histogram_dict.items():
273
- if "hist" in stats:
274
- # Normalize to probability distribution of each feature
275
- histograms[feature] = np.array(stats["hist"][0]) / stats["count"]
276
-
277
- # Convert the dictionary to pandas DataFrame
278
- histograms = pd.DataFrame(histograms)
279
-
280
- return histograms