mlrun 1.7.2rc3__py3-none-any.whl → 1.8.0rc1__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 (222) hide show
  1. mlrun/__init__.py +14 -12
  2. mlrun/__main__.py +3 -3
  3. mlrun/alerts/alert.py +19 -12
  4. mlrun/artifacts/__init__.py +0 -2
  5. mlrun/artifacts/base.py +34 -11
  6. mlrun/artifacts/dataset.py +16 -16
  7. mlrun/artifacts/manager.py +13 -13
  8. mlrun/artifacts/model.py +66 -53
  9. mlrun/common/constants.py +6 -0
  10. mlrun/common/formatters/__init__.py +1 -0
  11. mlrun/common/formatters/feature_set.py +1 -0
  12. mlrun/common/formatters/function.py +1 -0
  13. mlrun/common/formatters/model_endpoint.py +30 -0
  14. mlrun/common/formatters/pipeline.py +1 -2
  15. mlrun/common/model_monitoring/__init__.py +0 -3
  16. mlrun/common/model_monitoring/helpers.py +1 -1
  17. mlrun/common/runtimes/constants.py +1 -2
  18. mlrun/common/schemas/__init__.py +4 -2
  19. mlrun/common/schemas/artifact.py +0 -6
  20. mlrun/common/schemas/common.py +50 -0
  21. mlrun/common/schemas/model_monitoring/__init__.py +8 -1
  22. mlrun/common/schemas/model_monitoring/constants.py +62 -12
  23. mlrun/common/schemas/model_monitoring/model_endpoint_v2.py +149 -0
  24. mlrun/common/schemas/model_monitoring/model_endpoints.py +21 -5
  25. mlrun/common/schemas/partition.py +122 -0
  26. mlrun/config.py +43 -15
  27. mlrun/data_types/__init__.py +0 -2
  28. mlrun/data_types/data_types.py +0 -1
  29. mlrun/data_types/infer.py +3 -1
  30. mlrun/data_types/spark.py +4 -4
  31. mlrun/data_types/to_pandas.py +2 -11
  32. mlrun/datastore/__init__.py +0 -2
  33. mlrun/datastore/alibaba_oss.py +4 -1
  34. mlrun/datastore/azure_blob.py +4 -1
  35. mlrun/datastore/base.py +12 -4
  36. mlrun/datastore/datastore.py +9 -3
  37. mlrun/datastore/datastore_profile.py +1 -1
  38. mlrun/datastore/dbfs_store.py +4 -1
  39. mlrun/datastore/filestore.py +4 -1
  40. mlrun/datastore/google_cloud_storage.py +4 -1
  41. mlrun/datastore/hdfs.py +4 -1
  42. mlrun/datastore/inmem.py +4 -1
  43. mlrun/datastore/redis.py +4 -1
  44. mlrun/datastore/s3.py +4 -1
  45. mlrun/datastore/sources.py +51 -49
  46. mlrun/datastore/store_resources.py +0 -2
  47. mlrun/datastore/targets.py +22 -23
  48. mlrun/datastore/utils.py +2 -2
  49. mlrun/datastore/v3io.py +4 -1
  50. mlrun/datastore/wasbfs/fs.py +13 -12
  51. mlrun/db/base.py +126 -62
  52. mlrun/db/factory.py +3 -0
  53. mlrun/db/httpdb.py +767 -231
  54. mlrun/db/nopdb.py +126 -57
  55. mlrun/errors.py +2 -2
  56. mlrun/execution.py +55 -29
  57. mlrun/feature_store/__init__.py +0 -2
  58. mlrun/feature_store/api.py +40 -40
  59. mlrun/feature_store/common.py +9 -9
  60. mlrun/feature_store/feature_set.py +20 -18
  61. mlrun/feature_store/feature_vector.py +27 -24
  62. mlrun/feature_store/retrieval/base.py +14 -9
  63. mlrun/feature_store/retrieval/job.py +2 -1
  64. mlrun/feature_store/steps.py +2 -2
  65. mlrun/features.py +30 -13
  66. mlrun/frameworks/__init__.py +1 -2
  67. mlrun/frameworks/_common/__init__.py +1 -2
  68. mlrun/frameworks/_common/artifacts_library.py +2 -2
  69. mlrun/frameworks/_common/mlrun_interface.py +10 -6
  70. mlrun/frameworks/_common/model_handler.py +29 -27
  71. mlrun/frameworks/_common/producer.py +3 -1
  72. mlrun/frameworks/_dl_common/__init__.py +1 -2
  73. mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
  74. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
  75. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
  76. mlrun/frameworks/_ml_common/__init__.py +1 -2
  77. mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
  78. mlrun/frameworks/_ml_common/model_handler.py +21 -21
  79. mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
  80. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
  81. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  82. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  83. mlrun/frameworks/auto_mlrun/__init__.py +1 -2
  84. mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
  85. mlrun/frameworks/huggingface/__init__.py +1 -2
  86. mlrun/frameworks/huggingface/model_server.py +9 -9
  87. mlrun/frameworks/lgbm/__init__.py +47 -44
  88. mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
  89. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
  90. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
  91. mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
  92. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
  93. mlrun/frameworks/lgbm/model_handler.py +15 -11
  94. mlrun/frameworks/lgbm/model_server.py +11 -7
  95. mlrun/frameworks/lgbm/utils.py +2 -2
  96. mlrun/frameworks/onnx/__init__.py +1 -2
  97. mlrun/frameworks/onnx/dataset.py +3 -3
  98. mlrun/frameworks/onnx/mlrun_interface.py +2 -2
  99. mlrun/frameworks/onnx/model_handler.py +7 -5
  100. mlrun/frameworks/onnx/model_server.py +8 -6
  101. mlrun/frameworks/parallel_coordinates.py +11 -11
  102. mlrun/frameworks/pytorch/__init__.py +22 -23
  103. mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
  104. mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
  105. mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
  106. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
  107. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
  108. mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
  109. mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
  110. mlrun/frameworks/pytorch/model_handler.py +21 -17
  111. mlrun/frameworks/pytorch/model_server.py +13 -9
  112. mlrun/frameworks/sklearn/__init__.py +19 -18
  113. mlrun/frameworks/sklearn/estimator.py +2 -2
  114. mlrun/frameworks/sklearn/metric.py +3 -3
  115. mlrun/frameworks/sklearn/metrics_library.py +8 -6
  116. mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
  117. mlrun/frameworks/sklearn/model_handler.py +4 -3
  118. mlrun/frameworks/tf_keras/__init__.py +11 -12
  119. mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
  120. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
  121. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
  122. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
  123. mlrun/frameworks/tf_keras/model_handler.py +17 -13
  124. mlrun/frameworks/tf_keras/model_server.py +12 -8
  125. mlrun/frameworks/xgboost/__init__.py +19 -18
  126. mlrun/frameworks/xgboost/model_handler.py +13 -9
  127. mlrun/launcher/base.py +3 -4
  128. mlrun/launcher/local.py +1 -1
  129. mlrun/launcher/remote.py +1 -1
  130. mlrun/lists.py +4 -3
  131. mlrun/model.py +108 -44
  132. mlrun/model_monitoring/__init__.py +1 -2
  133. mlrun/model_monitoring/api.py +6 -6
  134. mlrun/model_monitoring/applications/_application_steps.py +13 -15
  135. mlrun/model_monitoring/applications/histogram_data_drift.py +41 -15
  136. mlrun/model_monitoring/applications/results.py +55 -3
  137. mlrun/model_monitoring/controller.py +185 -223
  138. mlrun/model_monitoring/db/_schedules.py +156 -0
  139. mlrun/model_monitoring/db/_stats.py +189 -0
  140. mlrun/model_monitoring/db/stores/__init__.py +1 -1
  141. mlrun/model_monitoring/db/stores/base/store.py +6 -65
  142. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -25
  143. mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -97
  144. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +2 -58
  145. mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -15
  146. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +6 -257
  147. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +9 -271
  148. mlrun/model_monitoring/db/tsdb/base.py +74 -22
  149. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +66 -35
  150. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
  151. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +284 -51
  152. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
  153. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -17
  154. mlrun/model_monitoring/helpers.py +97 -1
  155. mlrun/model_monitoring/model_endpoint.py +4 -2
  156. mlrun/model_monitoring/stream_processing.py +2 -2
  157. mlrun/model_monitoring/tracking_policy.py +10 -3
  158. mlrun/model_monitoring/writer.py +47 -26
  159. mlrun/package/__init__.py +3 -6
  160. mlrun/package/context_handler.py +1 -1
  161. mlrun/package/packager.py +12 -9
  162. mlrun/package/packagers/__init__.py +0 -2
  163. mlrun/package/packagers/default_packager.py +14 -11
  164. mlrun/package/packagers/numpy_packagers.py +16 -7
  165. mlrun/package/packagers/pandas_packagers.py +18 -18
  166. mlrun/package/packagers/python_standard_library_packagers.py +25 -11
  167. mlrun/package/packagers_manager.py +31 -14
  168. mlrun/package/utils/__init__.py +0 -3
  169. mlrun/package/utils/_pickler.py +6 -6
  170. mlrun/platforms/__init__.py +3 -3
  171. mlrun/platforms/iguazio.py +4 -1
  172. mlrun/projects/__init__.py +1 -6
  173. mlrun/projects/operations.py +27 -27
  174. mlrun/projects/pipelines.py +85 -215
  175. mlrun/projects/project.py +444 -158
  176. mlrun/run.py +9 -9
  177. mlrun/runtimes/__init__.py +1 -3
  178. mlrun/runtimes/base.py +13 -10
  179. mlrun/runtimes/daskjob.py +9 -9
  180. mlrun/runtimes/generators.py +2 -1
  181. mlrun/runtimes/kubejob.py +4 -5
  182. mlrun/runtimes/mpijob/__init__.py +0 -2
  183. mlrun/runtimes/mpijob/abstract.py +7 -6
  184. mlrun/runtimes/nuclio/api_gateway.py +7 -7
  185. mlrun/runtimes/nuclio/application/application.py +11 -11
  186. mlrun/runtimes/nuclio/function.py +14 -13
  187. mlrun/runtimes/nuclio/serving.py +9 -9
  188. mlrun/runtimes/pod.py +74 -29
  189. mlrun/runtimes/remotesparkjob.py +3 -2
  190. mlrun/runtimes/sparkjob/__init__.py +0 -2
  191. mlrun/runtimes/sparkjob/spark3job.py +21 -11
  192. mlrun/runtimes/utils.py +6 -5
  193. mlrun/serving/merger.py +6 -4
  194. mlrun/serving/remote.py +18 -17
  195. mlrun/serving/routers.py +27 -27
  196. mlrun/serving/server.py +1 -1
  197. mlrun/serving/states.py +76 -71
  198. mlrun/serving/utils.py +13 -2
  199. mlrun/serving/v1_serving.py +3 -2
  200. mlrun/serving/v2_serving.py +4 -4
  201. mlrun/track/__init__.py +1 -1
  202. mlrun/track/tracker.py +2 -2
  203. mlrun/track/trackers/mlflow_tracker.py +6 -5
  204. mlrun/utils/async_http.py +1 -1
  205. mlrun/utils/helpers.py +72 -28
  206. mlrun/utils/logger.py +104 -2
  207. mlrun/utils/notifications/notification/base.py +23 -4
  208. mlrun/utils/notifications/notification/console.py +1 -1
  209. mlrun/utils/notifications/notification/git.py +6 -6
  210. mlrun/utils/notifications/notification/ipython.py +5 -4
  211. mlrun/utils/notifications/notification/slack.py +1 -1
  212. mlrun/utils/notifications/notification/webhook.py +13 -17
  213. mlrun/utils/notifications/notification_pusher.py +23 -19
  214. mlrun/utils/regex.py +1 -1
  215. mlrun/utils/version/version.json +2 -2
  216. {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/METADATA +186 -186
  217. mlrun-1.8.0rc1.dist-info/RECORD +356 -0
  218. {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/WHEEL +1 -1
  219. mlrun-1.7.2rc3.dist-info/RECORD +0 -351
  220. {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/LICENSE +0 -0
  221. {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/entry_points.txt +0 -0
  222. {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,6 @@
14
14
 
15
15
  import datetime
16
16
  import typing
17
- import uuid
18
17
 
19
18
  import pandas as pd
20
19
  import sqlalchemy
@@ -75,9 +74,6 @@ class SQLStoreBase(StoreBase):
75
74
 
76
75
  def _init_tables(self):
77
76
  self._init_model_endpoints_table()
78
- self._init_application_results_table()
79
- self._init_application_metrics_table()
80
- self._init_monitoring_schedules_table()
81
77
 
82
78
  def _init_model_endpoints_table(self):
83
79
  self.model_endpoints_table = (
@@ -89,32 +85,6 @@ class SQLStoreBase(StoreBase):
89
85
  self.model_endpoints_table
90
86
  )
91
87
 
92
- def _init_application_results_table(self):
93
- self.application_results_table = (
94
- mlrun.model_monitoring.db.stores.sqldb.models._get_application_result_table(
95
- connection_string=self._sql_connection_string
96
- )
97
- )
98
- self._tables[mm_schemas.FileTargetKind.APP_RESULTS] = (
99
- self.application_results_table
100
- )
101
-
102
- def _init_application_metrics_table(self) -> None:
103
- self.application_metrics_table = mlrun.model_monitoring.db.stores.sqldb.models._get_application_metrics_table(
104
- connection_string=self._sql_connection_string
105
- )
106
- self._tables[mm_schemas.FileTargetKind.APP_METRICS] = (
107
- self.application_metrics_table
108
- )
109
-
110
- def _init_monitoring_schedules_table(self):
111
- self.MonitoringSchedulesTable = mlrun.model_monitoring.db.stores.sqldb.models._get_monitoring_schedules_table(
112
- connection_string=self._sql_connection_string
113
- )
114
- self._tables[mm_schemas.FileTargetKind.MONITORING_SCHEDULES] = (
115
- self.MonitoringSchedulesTable
116
- )
117
-
118
88
  def _write(self, table_name: str, event: dict[str, typing.Any]) -> None:
119
89
  """
120
90
  Create a new record in the SQL table.
@@ -272,12 +242,12 @@ class SQLStoreBase(StoreBase):
272
242
 
273
243
  def list_model_endpoints(
274
244
  self,
275
- model: str = None,
276
- function: str = None,
277
- labels: list[str] = None,
278
- top_level: bool = None,
279
- uids: list = None,
280
- include_stats: bool = None,
245
+ model: typing.Optional[str] = None,
246
+ function: typing.Optional[str] = None,
247
+ labels: typing.Optional[list[str]] = None,
248
+ top_level: typing.Optional[bool] = None,
249
+ uids: typing.Optional[list] = None,
250
+ include_stats: typing.Optional[bool] = None,
281
251
  ) -> list[dict[str, typing.Any]]:
282
252
  # Generate an empty model endpoints that will be filled afterwards with model endpoint dictionaries
283
253
  endpoint_list = []
@@ -348,182 +318,12 @@ class SQLStoreBase(StoreBase):
348
318
 
349
319
  return endpoint_list
350
320
 
351
- def write_application_event(
352
- self,
353
- event: dict[str, typing.Any],
354
- kind: mm_schemas.WriterEventKind = mm_schemas.WriterEventKind.RESULT,
355
- ) -> None:
356
- """
357
- Write a new application event in the target table.
358
-
359
- :param event: An event dictionary that represents the application result or metric,
360
- should be corresponded to the schema defined in the
361
- :py:class:`~mm_constants.constants.WriterEvent` object.
362
- :param kind: The type of the event, can be either "result" or "metric".
363
- """
364
-
365
- if kind == mm_schemas.WriterEventKind.METRIC:
366
- table = self.application_metrics_table
367
- table_name = mm_schemas.FileTargetKind.APP_METRICS
368
- elif kind == mm_schemas.WriterEventKind.RESULT:
369
- table = self.application_results_table
370
- table_name = mm_schemas.FileTargetKind.APP_RESULTS
371
- else:
372
- raise ValueError(f"Invalid {kind = }")
373
-
374
- application_result_uid = self._generate_application_result_uid(event, kind=kind)
375
- criteria = [table.uid == application_result_uid]
376
-
377
- application_record = self._get(table=table, criteria=criteria)
378
- if application_record:
379
- self._convert_to_datetime(
380
- event=event, key=mm_schemas.WriterEvent.START_INFER_TIME
381
- )
382
- self._convert_to_datetime(
383
- event=event, key=mm_schemas.WriterEvent.END_INFER_TIME
384
- )
385
- # Update an existing application result
386
- self._update(attributes=event, table=table, criteria=criteria)
387
- else:
388
- # Write a new application result
389
- event[mm_schemas.EventFieldType.UID] = application_result_uid
390
- self._write(table_name=table_name, event=event)
391
-
392
321
  @staticmethod
393
322
  def _convert_to_datetime(event: dict[str, typing.Any], key: str) -> None:
394
323
  if isinstance(event[key], str):
395
324
  event[key] = datetime.datetime.fromisoformat(event[key])
396
325
  event[key] = event[key].astimezone(tz=datetime.timezone.utc)
397
326
 
398
- @staticmethod
399
- def _generate_application_result_uid(
400
- event: dict[str, typing.Any],
401
- kind: mm_schemas.WriterEventKind = mm_schemas.WriterEventKind.RESULT,
402
- ) -> str:
403
- if kind == mm_schemas.WriterEventKind.RESULT:
404
- name = event[mm_schemas.ResultData.RESULT_NAME]
405
- else:
406
- name = event[mm_schemas.MetricData.METRIC_NAME]
407
- return "_".join(
408
- [
409
- event[mm_schemas.WriterEvent.ENDPOINT_ID],
410
- event[mm_schemas.WriterEvent.APPLICATION_NAME],
411
- name,
412
- ]
413
- )
414
-
415
- @staticmethod
416
- def _get_filter_criteria(
417
- *,
418
- table: sqlalchemy.orm.decl_api.DeclarativeMeta,
419
- endpoint_id: str,
420
- application_name: typing.Optional[str] = None,
421
- ) -> list[BinaryExpression]:
422
- """
423
- Return the filter criteria for the given endpoint_id and application_name.
424
- Note: the table object must include the relevant columns:
425
- `endpoint_id` and `application_name`.
426
- """
427
- criteria = [table.endpoint_id == endpoint_id]
428
- if application_name is not None:
429
- criteria.append(table.application_name == application_name)
430
- return criteria
431
-
432
- def get_last_analyzed(self, endpoint_id: str, application_name: str) -> int:
433
- """
434
- Get the last analyzed time for the provided model endpoint and application.
435
-
436
- :param endpoint_id: The unique id of the model endpoint.
437
- :param application_name: Registered application name.
438
-
439
- :return: Timestamp as a Unix time.
440
- :raise: MLRunNotFoundError if last analyzed value is not found.
441
- """
442
- monitoring_schedule_record = self._get(
443
- table=self.MonitoringSchedulesTable,
444
- criteria=self._get_filter_criteria(
445
- table=self.MonitoringSchedulesTable,
446
- endpoint_id=endpoint_id,
447
- application_name=application_name,
448
- ),
449
- )
450
- if not monitoring_schedule_record:
451
- raise mlrun.errors.MLRunNotFoundError(
452
- f"No last analyzed value has been found for {application_name} "
453
- f"that processes model endpoint {endpoint_id}"
454
- )
455
- return monitoring_schedule_record.last_analyzed
456
-
457
- def update_last_analyzed(
458
- self, endpoint_id: str, application_name: str, last_analyzed: int
459
- ):
460
- """
461
- Update the last analyzed time for the provided model endpoint and application.
462
-
463
- :param endpoint_id: The unique id of the model endpoint.
464
- :param application_name: Registered application name.
465
- :param last_analyzed: Timestamp as a Unix time that represents the last analyzed time of a certain
466
- application and model endpoint.
467
- """
468
- criteria = self._get_filter_criteria(
469
- table=self.MonitoringSchedulesTable,
470
- endpoint_id=endpoint_id,
471
- application_name=application_name,
472
- )
473
- monitoring_schedule_record = self._get(
474
- table=self.MonitoringSchedulesTable, criteria=criteria
475
- )
476
- if not monitoring_schedule_record:
477
- # Add a new record with last analyzed value
478
- self._write(
479
- table_name=mm_schemas.FileTargetKind.MONITORING_SCHEDULES,
480
- event={
481
- mm_schemas.SchedulingKeys.UID: uuid.uuid4().hex,
482
- mm_schemas.SchedulingKeys.APPLICATION_NAME: application_name,
483
- mm_schemas.SchedulingKeys.ENDPOINT_ID: endpoint_id,
484
- mm_schemas.SchedulingKeys.LAST_ANALYZED: last_analyzed,
485
- },
486
- )
487
-
488
- self._update(
489
- attributes={mm_schemas.SchedulingKeys.LAST_ANALYZED: last_analyzed},
490
- table=self.MonitoringSchedulesTable,
491
- criteria=criteria,
492
- )
493
-
494
- def _delete_last_analyzed(
495
- self, endpoint_id: str, application_name: typing.Optional[str] = None
496
- ) -> None:
497
- criteria = self._get_filter_criteria(
498
- table=self.MonitoringSchedulesTable,
499
- endpoint_id=endpoint_id,
500
- application_name=application_name,
501
- )
502
- # Delete the model endpoint record using sqlalchemy ORM
503
- self._delete(table=self.MonitoringSchedulesTable, criteria=criteria)
504
-
505
- def _delete_application_result(
506
- self, endpoint_id: str, application_name: typing.Optional[str] = None
507
- ) -> None:
508
- criteria = self._get_filter_criteria(
509
- table=self.application_results_table,
510
- endpoint_id=endpoint_id,
511
- application_name=application_name,
512
- )
513
- # Delete the relevant records from the results table
514
- self._delete(table=self.application_results_table, criteria=criteria)
515
-
516
- def _delete_application_metrics(
517
- self, endpoint_id: str, application_name: typing.Optional[str] = None
518
- ) -> None:
519
- criteria = self._get_filter_criteria(
520
- table=self.application_metrics_table,
521
- endpoint_id=endpoint_id,
522
- application_name=application_name,
523
- )
524
- # Delete the relevant records from the metrics table
525
- self._delete(table=self.application_metrics_table, criteria=criteria)
526
-
527
327
  def _create_tables_if_not_exist(self):
528
328
  self._init_tables()
529
329
 
@@ -593,12 +393,6 @@ class SQLStoreBase(StoreBase):
593
393
  endpoint_id=endpoint_id,
594
394
  project=self.project,
595
395
  )
596
- # Delete last analyzed records
597
- self._delete_last_analyzed(endpoint_id=endpoint_id)
598
-
599
- # Delete application results and metrics records
600
- self._delete_application_result(endpoint_id=endpoint_id)
601
- self._delete_application_metrics(endpoint_id=endpoint_id)
602
396
 
603
397
  # Delete model endpoint record
604
398
  self.delete_model_endpoint(endpoint_id=endpoint_id)
@@ -612,48 +406,3 @@ class SQLStoreBase(StoreBase):
612
406
  "Successfully deleted model monitoring endpoints resources from the SQL tables",
613
407
  project=self.project,
614
408
  )
615
-
616
- def get_model_endpoint_metrics(
617
- self, endpoint_id: str, type: mm_schemas.ModelEndpointMonitoringMetricType
618
- ) -> list[mm_schemas.ModelEndpointMonitoringMetric]:
619
- """
620
- Fetch the model endpoint metrics or results (according to `type`) for the
621
- requested endpoint.
622
- """
623
- logger.debug(
624
- "Fetching metrics for model endpoint",
625
- project=self.project,
626
- endpoint_id=endpoint_id,
627
- type=type,
628
- )
629
- if type == mm_schemas.ModelEndpointMonitoringMetricType.METRIC:
630
- table = self.application_metrics_table
631
- name_col = mm_schemas.MetricData.METRIC_NAME
632
- else:
633
- table = self.application_results_table
634
- name_col = mm_schemas.ResultData.RESULT_NAME
635
-
636
- # Note: the block below does not use self._get, as we need here all the
637
- # results, not only `one_or_none`.
638
- with sqlalchemy.orm.Session(self.engine) as session:
639
- metric_rows = (
640
- session.query(table) # pyright: ignore[reportOptionalCall]
641
- .filter(table.endpoint_id == endpoint_id)
642
- .all()
643
- )
644
-
645
- return [
646
- mm_schemas.ModelEndpointMonitoringMetric(
647
- project=self.project,
648
- app=metric_row.application_name,
649
- type=type,
650
- name=getattr(metric_row, name_col),
651
- full_name=mlrun.model_monitoring.helpers._compose_full_name(
652
- project=self.project,
653
- app=metric_row.application_name,
654
- type=type,
655
- name=getattr(metric_row, name_col),
656
- ),
657
- )
658
- for metric_row in metric_rows
659
- ]
@@ -11,11 +11,10 @@
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
- import http
14
+
15
15
  import json
16
16
  import typing
17
17
  from dataclasses import dataclass
18
- from http import HTTPStatus
19
18
 
20
19
  import v3io.dataplane
21
20
  import v3io.dataplane.output
@@ -34,14 +33,6 @@ fields_to_encode_decode = [
34
33
  mm_schemas.EventFieldType.CURRENT_STATS,
35
34
  ]
36
35
 
37
- _METRIC_FIELDS: list[str] = [
38
- mm_schemas.WriterEvent.APPLICATION_NAME.value,
39
- mm_schemas.MetricData.METRIC_NAME.value,
40
- mm_schemas.MetricData.METRIC_VALUE.value,
41
- mm_schemas.WriterEvent.START_INFER_TIME.value,
42
- mm_schemas.WriterEvent.END_INFER_TIME.value,
43
- ]
44
-
45
36
 
46
37
  class SchemaField(typing.TypedDict):
47
38
  name: str
@@ -55,37 +46,6 @@ class SchemaParams:
55
46
  fields: list[SchemaField]
56
47
 
57
48
 
58
- _RESULT_SCHEMA: list[SchemaField] = [
59
- SchemaField(
60
- name=mm_schemas.ResultData.RESULT_NAME,
61
- type=mm_schemas.GrafanaColumnType.STRING,
62
- nullable=False,
63
- )
64
- ]
65
-
66
- _METRIC_SCHEMA: list[SchemaField] = [
67
- SchemaField(
68
- name=mm_schemas.WriterEvent.APPLICATION_NAME,
69
- type=mm_schemas.GrafanaColumnType.STRING,
70
- nullable=False,
71
- ),
72
- SchemaField(
73
- name=mm_schemas.MetricData.METRIC_NAME,
74
- type=mm_schemas.GrafanaColumnType.STRING,
75
- nullable=False,
76
- ),
77
- ]
78
-
79
-
80
- _KIND_TO_SCHEMA_PARAMS: dict[mm_schemas.WriterEventKind, SchemaParams] = {
81
- mm_schemas.WriterEventKind.RESULT: SchemaParams(
82
- key=mm_schemas.WriterEvent.APPLICATION_NAME, fields=_RESULT_SCHEMA
83
- ),
84
- mm_schemas.WriterEventKind.METRIC: SchemaParams(
85
- key="metric_id", fields=_METRIC_SCHEMA
86
- ),
87
- }
88
-
89
49
  _EXCLUDE_SCHEMA_FILTER_EXPRESSION = '__name!=".#schema"'
90
50
 
91
51
 
@@ -227,12 +187,12 @@ class KVStoreBase(StoreBase):
227
187
 
228
188
  def list_model_endpoints(
229
189
  self,
230
- model: str = None,
231
- function: str = None,
232
- labels: list[str] = None,
233
- top_level: bool = None,
234
- uids: list = None,
235
- include_stats: bool = None,
190
+ model: typing.Optional[str] = None,
191
+ function: typing.Optional[str] = None,
192
+ labels: typing.Optional[list[str]] = None,
193
+ top_level: typing.Optional[bool] = None,
194
+ uids: typing.Optional[list] = None,
195
+ include_stats: typing.Optional[bool] = None,
236
196
  ) -> list[dict[str, typing.Any]]:
237
197
  # # Initialize an empty model endpoints list
238
198
  endpoint_list = []
@@ -339,135 +299,6 @@ class KVStoreBase(StoreBase):
339
299
  raise_for_status=v3io.dataplane.RaiseForStatus.never,
340
300
  )
341
301
 
342
- @staticmethod
343
- def _get_results_table_path(endpoint_id: str) -> str:
344
- return endpoint_id
345
-
346
- @staticmethod
347
- def _get_metrics_table_path(endpoint_id: str) -> str:
348
- return f"{endpoint_id}_metrics"
349
-
350
- def write_application_event(
351
- self,
352
- event: dict[str, typing.Any],
353
- kind: mm_schemas.WriterEventKind = mm_schemas.WriterEventKind.RESULT,
354
- ) -> None:
355
- """
356
- Write a new application event in the target table.
357
-
358
- :param event: An event dictionary that represents the application result, should be corresponded to the
359
- schema defined in the :py:class:`~mlrun.common.schemas.model_monitoring.constants.WriterEvent`
360
- object.
361
- :param kind: The type of the event, can be either "result" or "metric".
362
- """
363
-
364
- container = self.get_v3io_monitoring_apps_container(project_name=self.project)
365
- endpoint_id = event.pop(mm_schemas.WriterEvent.ENDPOINT_ID)
366
-
367
- if kind == mm_schemas.WriterEventKind.METRIC:
368
- table_path = self._get_metrics_table_path(endpoint_id)
369
- key = f"{event[mm_schemas.WriterEvent.APPLICATION_NAME]}.{event[mm_schemas.MetricData.METRIC_NAME]}"
370
- attributes = {event_key: event[event_key] for event_key in _METRIC_FIELDS}
371
- elif kind == mm_schemas.WriterEventKind.RESULT:
372
- table_path = self._get_results_table_path(endpoint_id)
373
- key = event.pop(mm_schemas.WriterEvent.APPLICATION_NAME)
374
- metric_name = event.pop(mm_schemas.ResultData.RESULT_NAME)
375
- attributes = {metric_name: self._encode_field(json.dumps(event))}
376
- else:
377
- raise ValueError(f"Invalid {kind = }")
378
-
379
- self.client.kv.update(
380
- container=container,
381
- table_path=table_path,
382
- key=key,
383
- attributes=attributes,
384
- )
385
-
386
- schema_file = self.client.kv.new_cursor(
387
- container=container,
388
- table_path=table_path,
389
- filter_expression='__name==".#schema"',
390
- )
391
-
392
- if not schema_file.all():
393
- logger.info(
394
- "Generating a new V3IO KV schema file",
395
- container=container,
396
- table_path=table_path,
397
- )
398
- self._generate_kv_schema(
399
- container=container, table_path=table_path, kind=kind
400
- )
401
- logger.info("Updated V3IO KV successfully", key=key)
402
-
403
- def _generate_kv_schema(
404
- self, *, container: str, table_path: str, kind: mm_schemas.WriterEventKind
405
- ) -> None:
406
- """Generate V3IO KV schema file which will be used by the model monitoring applications dashboard in Grafana."""
407
- schema_params = _KIND_TO_SCHEMA_PARAMS[kind]
408
- res = self.client.kv.create_schema(
409
- container=container,
410
- table_path=table_path,
411
- key=schema_params.key,
412
- fields=schema_params.fields,
413
- )
414
- if res.status_code != HTTPStatus.OK:
415
- raise mlrun.errors.MLRunBadRequestError(
416
- f"Couldn't infer schema for endpoint {table_path} which is required for Grafana dashboards"
417
- )
418
- else:
419
- logger.info("Generated V3IO KV schema successfully", table_path=table_path)
420
-
421
- def get_last_analyzed(self, endpoint_id: str, application_name: str) -> int:
422
- """
423
- Get the last analyzed time for the provided model endpoint and application.
424
-
425
- :param endpoint_id: The unique id of the model endpoint.
426
- :param application_name: Registered application name.
427
-
428
- :return: Timestamp as a Unix time.
429
- :raise: MLRunNotFoundError if last analyzed value is not found.
430
-
431
- """
432
- try:
433
- response = self.client.kv.get(
434
- container=self._get_monitoring_schedules_container(
435
- project_name=self.project
436
- ),
437
- table_path=endpoint_id,
438
- key=application_name,
439
- )
440
- return response.output.item[mm_schemas.SchedulingKeys.LAST_ANALYZED]
441
- except v3io.dataplane.response.HttpResponseError as err:
442
- if err.status_code == http.HTTPStatus.NOT_FOUND:
443
- logger.debug("Last analyzed time not found", err=err)
444
- raise mlrun.errors.MLRunNotFoundError(
445
- f"No last analyzed value has been found for {application_name} "
446
- f"that processes model endpoint {endpoint_id}",
447
- )
448
- logger.error("Error while getting last analyzed time", err=err)
449
- raise err
450
-
451
- def update_last_analyzed(
452
- self, endpoint_id: str, application_name: str, last_analyzed: int
453
- ):
454
- """
455
- Update the last analyzed time for the provided model endpoint and application.
456
-
457
- :param endpoint_id: The unique id of the model endpoint.
458
- :param application_name: Registered application name.
459
- :param last_analyzed: Timestamp as a Unix time that represents the last analyzed time of a certain
460
- application and model endpoint.
461
- """
462
- self.client.kv.put(
463
- container=self._get_monitoring_schedules_container(
464
- project_name=self.project
465
- ),
466
- table_path=endpoint_id,
467
- key=application_name,
468
- attributes={mm_schemas.SchedulingKeys.LAST_ANALYZED: last_analyzed},
469
- )
470
-
471
302
  def _generate_tsdb_paths(self) -> tuple[str, str]:
472
303
  """Generate a short path to the TSDB resources and a filtered path for the frames object
473
304
  :return: A tuple of:
@@ -524,8 +355,8 @@ class KVStoreBase(StoreBase):
524
355
  @staticmethod
525
356
  def _build_kv_cursor_filter_expression(
526
357
  project: str,
527
- function: str = None,
528
- model: str = None,
358
+ function: typing.Optional[str] = None,
359
+ model: typing.Optional[str] = None,
529
360
  top_level: bool = False,
530
361
  ) -> str:
531
362
  """
@@ -631,96 +462,3 @@ class KVStoreBase(StoreBase):
631
462
  @staticmethod
632
463
  def _get_monitoring_schedules_container(project_name: str) -> str:
633
464
  return f"users/pipelines/{project_name}/monitoring-schedules/functions"
634
-
635
- def _extract_results_from_items(
636
- self, app_items: list[dict[str, str]]
637
- ) -> list[mm_schemas.ModelEndpointMonitoringMetric]:
638
- """Assuming .#schema items are filtered out"""
639
- metrics: list[mm_schemas.ModelEndpointMonitoringMetric] = []
640
- for app_item in app_items:
641
- app_name = app_item.pop("__name")
642
- for result_name in app_item:
643
- metrics.append(
644
- mm_schemas.ModelEndpointMonitoringMetric(
645
- project=self.project,
646
- app=app_name,
647
- type=mm_schemas.ModelEndpointMonitoringMetricType.RESULT,
648
- name=result_name,
649
- full_name=mm_schemas.model_endpoints._compose_full_name(
650
- project=self.project, app=app_name, name=result_name
651
- ),
652
- )
653
- )
654
- return metrics
655
-
656
- def _extract_metrics_from_items(
657
- self, result_items: list[dict[str, str]]
658
- ) -> list[mm_schemas.ModelEndpointMonitoringMetric]:
659
- metrics: list[mm_schemas.ModelEndpointMonitoringMetric] = []
660
- logger.debug("Result items", result_items=result_items)
661
- for result_item in result_items:
662
- app = result_item[mm_schemas.WriterEvent.APPLICATION_NAME]
663
- name = result_item[mm_schemas.MetricData.METRIC_NAME]
664
- metrics.append(
665
- mm_schemas.ModelEndpointMonitoringMetric(
666
- project=self.project,
667
- app=app,
668
- type=mm_schemas.ModelEndpointMonitoringMetricType.METRIC,
669
- name=name,
670
- full_name=mm_schemas.model_endpoints._compose_full_name(
671
- project=self.project,
672
- app=app,
673
- name=name,
674
- type=mm_schemas.ModelEndpointMonitoringMetricType.METRIC,
675
- ),
676
- )
677
- )
678
- return metrics
679
-
680
- def get_model_endpoint_metrics(
681
- self, endpoint_id: str, type: mm_schemas.ModelEndpointMonitoringMetricType
682
- ) -> list[mm_schemas.ModelEndpointMonitoringMetric]:
683
- """Get model monitoring results and metrics on the endpoint"""
684
- metrics: list[mm_schemas.ModelEndpointMonitoringMetric] = []
685
- container = self.get_v3io_monitoring_apps_container(self.project)
686
- if type == mm_schemas.ModelEndpointMonitoringMetricType.METRIC:
687
- table_path = self._get_metrics_table_path(endpoint_id)
688
- items_extractor = self._extract_metrics_from_items
689
- elif type == mm_schemas.ModelEndpointMonitoringMetricType.RESULT:
690
- table_path = self._get_results_table_path(endpoint_id)
691
- items_extractor = self._extract_results_from_items
692
- else:
693
- raise ValueError(f"Invalid metric {type = }")
694
-
695
- def scan(
696
- marker: typing.Optional[str] = None,
697
- ) -> v3io.dataplane.response.Response:
698
- # TODO: Use AIO client: `v3io.aio.dataplane.client.Client`
699
- return self.client.kv.scan(
700
- container=container,
701
- table_path=table_path,
702
- marker=marker,
703
- filter_expression=_EXCLUDE_SCHEMA_FILTER_EXPRESSION,
704
- )
705
-
706
- try:
707
- response = scan()
708
- except v3io.dataplane.response.HttpResponseError as err:
709
- if err.status_code == HTTPStatus.NOT_FOUND:
710
- logger.warning(
711
- f"Attempt getting {type}s - no data. Check the "
712
- "project name, endpoint, or wait for the applications to start.",
713
- container=container,
714
- table_path=table_path,
715
- )
716
- return []
717
- raise
718
-
719
- while True:
720
- output = typing.cast(v3io.dataplane.output.GetItemsOutput, response.output)
721
- metrics.extend(items_extractor(output.items))
722
- if output.last:
723
- break
724
- response = scan(marker=output.next_marker)
725
-
726
- return metrics