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
mlrun/db/httpdb.py CHANGED
@@ -525,10 +525,6 @@ class HTTPRunDB(RunDBInterface):
525
525
  server_cfg.get("external_platform_tracking")
526
526
  or config.external_platform_tracking
527
527
  )
528
- config.model_endpoint_monitoring.store_type = (
529
- server_cfg.get("model_endpoint_monitoring_store_type")
530
- or config.model_endpoint_monitoring.store_type
531
- )
532
528
  config.model_endpoint_monitoring.endpoint_store_connection = (
533
529
  server_cfg.get("model_endpoint_monitoring_endpoint_store_connection")
534
530
  or config.model_endpoint_monitoring.endpoint_store_connection
@@ -1015,7 +1011,7 @@ class HTTPRunDB(RunDBInterface):
1015
1011
  "format": format_,
1016
1012
  "tag": tag,
1017
1013
  "tree": tree,
1018
- "uid": uid,
1014
+ "object-uid": uid,
1019
1015
  }
1020
1016
  if iter is not None:
1021
1017
  params["iter"] = str(iter)
@@ -1033,6 +1029,7 @@ class HTTPRunDB(RunDBInterface):
1033
1029
  mlrun.common.schemas.artifact.ArtifactsDeletionStrategies.metadata_only
1034
1030
  ),
1035
1031
  secrets: dict = None,
1032
+ iter=None,
1036
1033
  ):
1037
1034
  """Delete an artifact.
1038
1035
 
@@ -1050,7 +1047,8 @@ class HTTPRunDB(RunDBInterface):
1050
1047
  "key": key,
1051
1048
  "tag": tag,
1052
1049
  "tree": tree,
1053
- "uid": uid,
1050
+ "object-uid": uid,
1051
+ "iter": iter,
1054
1052
  "deletion_strategy": deletion_strategy,
1055
1053
  }
1056
1054
  error = f"del artifact {project}/{key}"
@@ -1069,8 +1067,8 @@ class HTTPRunDB(RunDBInterface):
1069
1067
  project=None,
1070
1068
  tag=None,
1071
1069
  labels: Optional[Union[dict[str, str], list[str]]] = None,
1072
- since=None,
1073
- until=None,
1070
+ since: Optional[datetime] = None,
1071
+ until: Optional[datetime] = None,
1074
1072
  iter: int = None,
1075
1073
  best_iteration: bool = False,
1076
1074
  kind: str = None,
@@ -1100,8 +1098,8 @@ class HTTPRunDB(RunDBInterface):
1100
1098
  :param tag: Return artifacts assigned this tag.
1101
1099
  :param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
1102
1100
  a list of "label=value" (match label key and value) or "label" (match just label key) strings.
1103
- :param since: Not in use in :py:class:`HTTPRunDB`.
1104
- :param until: Not in use in :py:class:`HTTPRunDB`.
1101
+ :param since: Return artifacts updated after this date (as datetime object).
1102
+ :param until: Return artifacts updated before this date (as datetime object).
1105
1103
  :param iter: Return artifacts from a specific iteration (where ``iter=0`` means the root iteration). If
1106
1104
  ``None`` (default) return artifacts from all iterations.
1107
1105
  :param best_iteration: Returns the artifact which belongs to the best iteration of a given run, in the case of
@@ -1135,6 +1133,8 @@ class HTTPRunDB(RunDBInterface):
1135
1133
  "format": format_,
1136
1134
  "producer_uri": producer_uri,
1137
1135
  "limit": limit,
1136
+ "since": datetime_to_iso(since),
1137
+ "until": datetime_to_iso(until),
1138
1138
  }
1139
1139
  error = "list artifacts"
1140
1140
  endpoint_path = f"projects/{project}/artifacts"
@@ -1251,13 +1251,17 @@ class HTTPRunDB(RunDBInterface):
1251
1251
  function_name=name,
1252
1252
  )
1253
1253
 
1254
- def list_functions(self, name=None, project=None, tag=None, labels=None):
1254
+ def list_functions(
1255
+ self, name=None, project=None, tag=None, labels=None, since=None, until=None
1256
+ ):
1255
1257
  """Retrieve a list of functions, filtered by specific criteria.
1256
1258
 
1257
1259
  :param name: Return only functions with a specific name.
1258
1260
  :param project: Return functions belonging to this project. If not specified, the default project is used.
1259
- :param tag: Return function versions with specific tags.
1261
+ :param tag: Return function versions with specific tags. To return only tagged functions, set tag to ``"*"``.
1260
1262
  :param labels: Return functions that have specific labels assigned to them.
1263
+ :param since: Return functions updated after this date (as datetime object).
1264
+ :param until: Return functions updated before this date (as datetime object).
1261
1265
  :returns: List of function objects (as dictionary).
1262
1266
  """
1263
1267
  project = project or config.default_project
@@ -1265,6 +1269,8 @@ class HTTPRunDB(RunDBInterface):
1265
1269
  "name": name,
1266
1270
  "tag": tag,
1267
1271
  "label": labels or [],
1272
+ "since": datetime_to_iso(since),
1273
+ "until": datetime_to_iso(until),
1268
1274
  }
1269
1275
  error = "list functions"
1270
1276
  path = f"projects/{project}/functions"
@@ -1364,20 +1370,14 @@ class HTTPRunDB(RunDBInterface):
1364
1370
  :returns: :py:class:`~mlrun.common.schemas.GroupedByProjectRuntimeResourcesOutput` listing the runtime resources
1365
1371
  that were removed.
1366
1372
  """
1367
- if grace_period is None:
1368
- grace_period = config.runtime_resources_deletion_grace_period
1369
- logger.info(
1370
- "Using default grace period for runtime resources deletion",
1371
- grace_period=grace_period,
1372
- )
1373
-
1374
1373
  params = {
1375
1374
  "label-selector": label_selector,
1376
1375
  "kind": kind,
1377
1376
  "object-id": object_id,
1378
1377
  "force": force,
1379
- "grace-period": grace_period,
1380
1378
  }
1379
+ if grace_period is not None:
1380
+ params["grace-period"] = grace_period
1381
1381
  error = "Failed deleting runtime resources"
1382
1382
  project_path = project if project else "*"
1383
1383
  response = self.api_call(
@@ -1676,7 +1676,7 @@ class HTTPRunDB(RunDBInterface):
1676
1676
  last_log_timestamp = float(
1677
1677
  resp.headers.get("x-mlrun-last-timestamp", "0.0")
1678
1678
  )
1679
- if func.kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes():
1679
+ if func.kind in mlrun.runtimes.RuntimeKinds.pure_nuclio_deployed_runtimes():
1680
1680
  mlrun.runtimes.nuclio.function.enrich_nuclio_function_from_headers(
1681
1681
  func, resp.headers
1682
1682
  )
@@ -2235,6 +2235,9 @@ class HTTPRunDB(RunDBInterface):
2235
2235
  partition_order: Union[
2236
2236
  mlrun.common.schemas.OrderType, str
2237
2237
  ] = mlrun.common.schemas.OrderType.desc,
2238
+ format_: Union[
2239
+ str, mlrun.common.formatters.FeatureSetFormat
2240
+ ] = mlrun.common.formatters.FeatureSetFormat.full,
2238
2241
  ) -> list[FeatureSet]:
2239
2242
  """Retrieve a list of feature-sets matching the criteria provided.
2240
2243
 
@@ -2252,6 +2255,9 @@ class HTTPRunDB(RunDBInterface):
2252
2255
  :param partition_sort_by: What field to sort the results by, within each partition defined by `partition_by`.
2253
2256
  Currently the only allowed value are `created` and `updated`.
2254
2257
  :param partition_order: Order of sorting within partitions - `asc` or `desc`. Default is `desc`.
2258
+ :param format_: Format of the results. Possible values are:
2259
+ - ``minimal`` - Return minimal feature set objects, not including stats and preview for each feature set.
2260
+ - ``full`` - Return full feature set objects.
2255
2261
  :returns: List of matching :py:class:`~mlrun.feature_store.FeatureSet` objects.
2256
2262
  """
2257
2263
 
@@ -2264,6 +2270,7 @@ class HTTPRunDB(RunDBInterface):
2264
2270
  "entity": entities or [],
2265
2271
  "feature": features or [],
2266
2272
  "label": labels or [],
2273
+ "format": format_,
2267
2274
  }
2268
2275
  if partition_by:
2269
2276
  params.update(
@@ -2747,7 +2754,7 @@ class HTTPRunDB(RunDBInterface):
2747
2754
  deletion_strategy: Union[
2748
2755
  str, mlrun.common.schemas.DeletionStrategy
2749
2756
  ] = mlrun.common.schemas.DeletionStrategy.default(),
2750
- ):
2757
+ ) -> None:
2751
2758
  """Delete a project.
2752
2759
 
2753
2760
  :param name: Name of the project to delete.
@@ -2766,7 +2773,7 @@ class HTTPRunDB(RunDBInterface):
2766
2773
  "DELETE", f"projects/{name}", error_message, headers=headers, version="v2"
2767
2774
  )
2768
2775
  if response.status_code == http.HTTPStatus.ACCEPTED:
2769
- logger.info("Project is being deleted", project_name=name)
2776
+ logger.info("Waiting for project to be deleted", project_name=name)
2770
2777
  background_task = mlrun.common.schemas.BackgroundTask(**response.json())
2771
2778
  background_task = self._wait_for_background_task_to_reach_terminal_state(
2772
2779
  background_task.metadata.name
@@ -2776,10 +2783,17 @@ class HTTPRunDB(RunDBInterface):
2776
2783
  == mlrun.common.schemas.BackgroundTaskState.succeeded
2777
2784
  ):
2778
2785
  logger.info("Project deleted", project_name=name)
2779
- return
2786
+ elif (
2787
+ background_task.status.state
2788
+ == mlrun.common.schemas.BackgroundTaskState.failed
2789
+ ):
2790
+ logger.error(
2791
+ "Project deletion failed",
2792
+ project_name=name,
2793
+ error=background_task.status.error,
2794
+ )
2780
2795
  elif response.status_code == http.HTTPStatus.NO_CONTENT:
2781
2796
  logger.info("Project deleted", project_name=name)
2782
- return
2783
2797
 
2784
2798
  def store_project(
2785
2799
  self,
@@ -3370,7 +3384,7 @@ class HTTPRunDB(RunDBInterface):
3370
3384
  By default, the image is mlrun/mlrun.
3371
3385
  """
3372
3386
  self.api_call(
3373
- method=mlrun.common.types.HTTPMethod.POST,
3387
+ method=mlrun.common.types.HTTPMethod.PATCH,
3374
3388
  path=f"projects/{project}/model-monitoring/model-monitoring-controller",
3375
3389
  params={
3376
3390
  "base_period": base_period,
@@ -3465,7 +3479,7 @@ class HTTPRunDB(RunDBInterface):
3465
3479
  if response.status_code == http.HTTPStatus.ACCEPTED:
3466
3480
  if delete_resources:
3467
3481
  logger.info(
3468
- "Model Monitoring is being disable",
3482
+ "Model Monitoring is being disabled",
3469
3483
  project_name=project,
3470
3484
  )
3471
3485
  if delete_user_applications:
@@ -3544,17 +3558,19 @@ class HTTPRunDB(RunDBInterface):
3544
3558
  self,
3545
3559
  project: str,
3546
3560
  credentials: dict[str, str],
3561
+ replace_creds: bool,
3547
3562
  ) -> None:
3548
3563
  """
3549
3564
  Set the credentials for the model monitoring application.
3550
3565
 
3551
3566
  :param project: Project name.
3552
3567
  :param credentials: Credentials to set.
3568
+ :param replace_creds: If True, will override the existing credentials.
3553
3569
  """
3554
3570
  self.api_call(
3555
3571
  method=mlrun.common.types.HTTPMethod.POST,
3556
3572
  path=f"projects/{project}/model-monitoring/set-model-monitoring-credentials",
3557
- params={**credentials},
3573
+ params={**credentials, "replace_creds": replace_creds},
3558
3574
  )
3559
3575
 
3560
3576
  def create_hub_source(
@@ -4181,6 +4197,9 @@ class HTTPRunDB(RunDBInterface):
4181
4197
  :param event_data: The data of the event.
4182
4198
  :param project: The project that the event belongs to.
4183
4199
  """
4200
+ if mlrun.mlconf.alerts.mode == mlrun.common.schemas.alert.AlertsModes.disabled:
4201
+ logger.warning("Alerts are disabled, event will not be generated")
4202
+
4184
4203
  project = project or config.default_project
4185
4204
  endpoint_path = f"projects/{project}/events/{name}"
4186
4205
  error_message = f"post event {project}/events/{name}"
@@ -4204,6 +4223,14 @@ class HTTPRunDB(RunDBInterface):
4204
4223
  :param project: The project that the alert belongs to.
4205
4224
  :returns: The created/modified alert.
4206
4225
  """
4226
+ if not alert_data:
4227
+ raise mlrun.errors.MLRunInvalidArgumentError("Alert data must be provided")
4228
+
4229
+ if mlrun.mlconf.alerts.mode == mlrun.common.schemas.alert.AlertsModes.disabled:
4230
+ logger.warning(
4231
+ "Alerts are disabled, alert will still be stored but will not be triggered"
4232
+ )
4233
+
4207
4234
  project = project or config.default_project
4208
4235
  endpoint_path = f"projects/{project}/alerts/{alert_name}"
4209
4236
  error_message = f"put alert {project}/alerts/{alert_name}"
@@ -4212,6 +4239,8 @@ class HTTPRunDB(RunDBInterface):
4212
4239
  if isinstance(alert_data, AlertConfig)
4213
4240
  else AlertConfig.from_dict(alert_data)
4214
4241
  )
4242
+ # Validation is necessary here because users can directly invoke this function
4243
+ # through `mlrun.get_run_db().store_alert_config()`.
4215
4244
  alert_instance.validate_required_fields()
4216
4245
 
4217
4246
  alert_data = alert_instance.to_dict()
mlrun/db/nopdb.py CHANGED
@@ -21,6 +21,7 @@ import mlrun.common.formatters
21
21
  import mlrun.common.runtimes.constants
22
22
  import mlrun.common.schemas
23
23
  import mlrun.errors
24
+ import mlrun.lists
24
25
 
25
26
  from ..config import config
26
27
  from ..utils import logger
@@ -73,6 +74,22 @@ class NopDB(RunDBInterface):
73
74
  def abort_run(self, uid, project="", iter=0, timeout=45, status_text=""):
74
75
  pass
75
76
 
77
+ def list_runtime_resources(
78
+ self,
79
+ project: Optional[str] = None,
80
+ label_selector: Optional[str] = None,
81
+ kind: Optional[str] = None,
82
+ object_id: Optional[str] = None,
83
+ group_by: Optional[
84
+ mlrun.common.schemas.ListRuntimeResourcesGroupByField
85
+ ] = None,
86
+ ) -> Union[
87
+ mlrun.common.schemas.RuntimeResourcesOutput,
88
+ mlrun.common.schemas.GroupedByJobRuntimeResourcesOutput,
89
+ mlrun.common.schemas.GroupedByProjectRuntimeResourcesOutput,
90
+ ]:
91
+ return []
92
+
76
93
  def read_run(
77
94
  self,
78
95
  uid,
@@ -108,7 +125,7 @@ class NopDB(RunDBInterface):
108
125
  max_partitions: int = 0,
109
126
  with_notifications: bool = False,
110
127
  ):
111
- pass
128
+ return mlrun.lists.RunList()
112
129
 
113
130
  def del_run(self, uid, project="", iter=0):
114
131
  pass
@@ -149,7 +166,7 @@ class NopDB(RunDBInterface):
149
166
  format_: mlrun.common.formatters.ArtifactFormat = mlrun.common.formatters.ArtifactFormat.full,
150
167
  limit: int = None,
151
168
  ):
152
- pass
169
+ return mlrun.lists.ArtifactList()
153
170
 
154
171
  def del_artifact(
155
172
  self,
@@ -162,6 +179,7 @@ class NopDB(RunDBInterface):
162
179
  mlrun.common.schemas.artifact.ArtifactsDeletionStrategies.metadata_only
163
180
  ),
164
181
  secrets: dict = None,
182
+ iter=None,
165
183
  ):
166
184
  pass
167
185
 
@@ -177,8 +195,10 @@ class NopDB(RunDBInterface):
177
195
  def delete_function(self, name: str, project: str = ""):
178
196
  pass
179
197
 
180
- def list_functions(self, name=None, project="", tag="", labels=None):
181
- pass
198
+ def list_functions(
199
+ self, name=None, project="", tag="", labels=None, since=None, until=None
200
+ ):
201
+ return []
182
202
 
183
203
  def tag_objects(
184
204
  self,
@@ -306,6 +326,9 @@ class NopDB(RunDBInterface):
306
326
  partition_order: Union[
307
327
  mlrun.common.schemas.OrderType, str
308
328
  ] = mlrun.common.schemas.OrderType.desc,
329
+ format_: Union[
330
+ str, mlrun.common.formatters.FeatureSetFormat
331
+ ] = mlrun.common.formatters.FeatureSetFormat.full,
309
332
  ) -> list[dict]:
310
333
  pass
311
334
 
@@ -418,7 +441,7 @@ class NopDB(RunDBInterface):
418
441
  ] = mlrun.common.formatters.PipelineFormat.metadata_only,
419
442
  page_size: int = None,
420
443
  ) -> mlrun.common.schemas.PipelinesOutput:
421
- pass
444
+ return mlrun.common.schemas.PipelinesOutput(runs=[], total_size=0)
422
445
 
423
446
  def create_project_secrets(
424
447
  self,
@@ -737,6 +760,7 @@ class NopDB(RunDBInterface):
737
760
  self,
738
761
  project: str,
739
762
  credentials: dict[str, str],
763
+ replace_creds: bool,
740
764
  ) -> None:
741
765
  pass
742
766
 
mlrun/errors.py CHANGED
@@ -29,11 +29,14 @@ class MLRunBaseError(Exception):
29
29
  pass
30
30
 
31
31
 
32
- class MLRunTaskNotReady(MLRunBaseError):
32
+ class MLRunTaskNotReadyError(MLRunBaseError):
33
33
  """indicate we are trying to read a value which is not ready
34
34
  or need to come from a job which is in progress"""
35
35
 
36
36
 
37
+ MLRunTaskNotReady = MLRunTaskNotReadyError # kept for BC only
38
+
39
+
37
40
  class MLRunHTTPError(MLRunBaseError, requests.HTTPError):
38
41
  def __init__(
39
42
  self,
@@ -137,7 +140,13 @@ def err_to_str(err):
137
140
  error_strings.append(err_msg)
138
141
  err = err.__cause__
139
142
 
140
- return ", caused by: ".join(error_strings)
143
+ err_msg = ", caused by: ".join(error_strings)
144
+
145
+ # in case the error string is longer than 32k, we truncate it
146
+ # the truncation takes the first 16k, then the last 16k characters
147
+ if len(err_msg) > 32_000:
148
+ err_msg = err_msg[:16_000] + "...truncated..." + err_msg[-16_000:]
149
+ return err_msg
141
150
 
142
151
 
143
152
  # Specific Errors
@@ -205,7 +214,15 @@ class MLRunTimeoutError(MLRunHTTPStatusError, TimeoutError):
205
214
  error_status_code = HTTPStatus.GATEWAY_TIMEOUT.value
206
215
 
207
216
 
208
- class MLRunInvalidMMStoreType(MLRunHTTPStatusError, ValueError):
217
+ class MLRunInvalidMMStoreTypeError(MLRunHTTPStatusError, ValueError):
218
+ error_status_code = HTTPStatus.BAD_REQUEST.value
219
+
220
+
221
+ class MLRunStreamConnectionFailureError(MLRunHTTPStatusError, ValueError):
222
+ error_status_code = HTTPStatus.BAD_REQUEST.value
223
+
224
+
225
+ class MLRunTSDBConnectionFailureError(MLRunHTTPStatusError, ValueError):
209
226
  error_status_code = HTTPStatus.BAD_REQUEST.value
210
227
 
211
228
 
mlrun/execution.py CHANGED
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import logging
15
16
  import os
16
17
  import uuid
17
18
  from copy import deepcopy
@@ -23,6 +24,7 @@ from dateutil import parser
23
24
 
24
25
  import mlrun
25
26
  import mlrun.common.constants as mlrun_constants
27
+ import mlrun.common.formatters
26
28
  from mlrun.artifacts import ModelArtifact
27
29
  from mlrun.datastore.store_resources import get_store_resource
28
30
  from mlrun.errors import MLRunInvalidArgumentError
@@ -78,7 +80,6 @@ class MLClientCtx:
78
80
  self._tmpfile = tmp
79
81
  self._logger = log_stream or logger
80
82
  self._log_level = "info"
81
- self._matrics_db = None
82
83
  self._autocommit = autocommit
83
84
  self._notifications = []
84
85
  self._state_thresholds = {}
@@ -103,8 +104,7 @@ class MLClientCtx:
103
104
  self._error = None
104
105
  self._commit = ""
105
106
  self._host = None
106
- self._start_time = now_date()
107
- self._last_update = now_date()
107
+ self._start_time = self._last_update = now_date()
108
108
  self._iteration_results = None
109
109
  self._children = []
110
110
  self._parent = None
@@ -170,6 +170,8 @@ class MLClientCtx:
170
170
  @log_level.setter
171
171
  def log_level(self, value: str):
172
172
  """Set the logging level, e.g. 'debug', 'info', 'error'"""
173
+ level = logging.getLevelName(value.upper())
174
+ self._logger.set_logger_level(level)
173
175
  self._log_level = value
174
176
 
175
177
  @property
@@ -337,7 +339,7 @@ class MLClientCtx:
337
339
  "name": self.name,
338
340
  "kind": "run",
339
341
  "uri": uri,
340
- "owner": get_in(self._labels, "owner"),
342
+ "owner": get_in(self._labels, mlrun_constants.MLRunInternalLabels.owner),
341
343
  }
342
344
  if mlrun_constants.MLRunInternalLabels.workflow in self._labels:
343
345
  resp[mlrun_constants.MLRunInternalLabels.workflow] = self._labels[
@@ -633,7 +635,9 @@ class MLClientCtx:
633
635
  :param viewer: Kubeflow viewer type
634
636
  :param target_path: Absolute target path (instead of using artifact_path + local_path)
635
637
  :param src_path: Deprecated, use local_path
636
- :param upload: Upload to datastore (default is True)
638
+ :param upload: Whether to upload the artifact to the datastore. If not provided, and the `local_path`
639
+ is not a directory, upload occurs by default. Directories are uploaded only when this
640
+ flag is explicitly set to `True`.
637
641
  :param labels: A set of key/value labels to tag the artifact with
638
642
  :param format: Optional, format to use (e.g. csv, parquet, ..)
639
643
  :param db_key: The key to use in the artifact DB table, by default its run name + '_' + key
@@ -923,6 +927,43 @@ class MLClientCtx:
923
927
  updates, self._uid, self.project, iter=self._iteration
924
928
  )
925
929
 
930
+ def get_notifications(self, unmask_secret_params=False):
931
+ """
932
+ Get the list of notifications
933
+
934
+ :param unmask_secret_params: Used as a workaround for sending notification from workflow-runner.
935
+ When used, if the notification will be saved again a new secret will be created.
936
+ """
937
+
938
+ # Get the full notifications from the DB since the run context does not contain the params due to bloating
939
+ run = self._rundb.read_run(
940
+ self.uid, format_=mlrun.common.formatters.RunFormat.notifications
941
+ )
942
+
943
+ notifications = []
944
+ for notification in run["spec"]["notifications"]:
945
+ notification: mlrun.model.Notification = mlrun.model.Notification.from_dict(
946
+ notification
947
+ )
948
+ # Fill the secret params from the project secret. We cannot use the server side internal secret mechanism
949
+ # here as it is the client side.
950
+ # TODO: This is a workaround to allow the notification to get the secret params from project secret
951
+ # instead of getting them from the internal project secret that should be mounted.
952
+ # We should mount the internal project secret that was created to the workflow-runner
953
+ # and get the secret from there.
954
+ if unmask_secret_params:
955
+ try:
956
+ notification.enrich_unmasked_secret_params_from_project_secret()
957
+ notifications.append(notification)
958
+ except mlrun.errors.MLRunValueError:
959
+ logger.warning(
960
+ "Failed to fill secret params from project secret for notification."
961
+ "Skip this notification.",
962
+ notification=notification.name,
963
+ )
964
+
965
+ return notifications
966
+
926
967
  def to_dict(self):
927
968
  """Convert the run context to a dictionary"""
928
969
 
@@ -230,6 +230,11 @@ def _get_offline_features(
230
230
  "entity_timestamp_column param "
231
231
  "can not be specified without entity_rows param"
232
232
  )
233
+ if isinstance(target, BaseStoreTarget) and not target.support_pandas:
234
+ raise mlrun.errors.MLRunInvalidArgumentError(
235
+ f"get_offline_features does not support targets that do not support pandas engine."
236
+ f" Target kind: {target.kind}"
237
+ )
233
238
 
234
239
  if isinstance(feature_vector, FeatureVector):
235
240
  update_stats = True
@@ -1032,6 +1037,8 @@ def _ingest_with_spark(
1032
1037
  try:
1033
1038
  import pyspark.sql
1034
1039
 
1040
+ from mlrun.datastore.spark_utils import check_special_columns_exists
1041
+
1035
1042
  if spark is None or spark is True:
1036
1043
  # create spark context
1037
1044
 
@@ -1044,13 +1051,13 @@ def _ingest_with_spark(
1044
1051
 
1045
1052
  spark = (
1046
1053
  pyspark.sql.SparkSession.builder.appName(session_name)
1054
+ .config("spark.driver.memory", "2g")
1047
1055
  .config("spark.sql.session.timeZone", "UTC")
1048
1056
  .getOrCreate()
1049
1057
  )
1050
1058
  created_spark_context = True
1051
1059
 
1052
1060
  timestamp_key = featureset.spec.timestamp_key
1053
-
1054
1061
  if isinstance(source, pd.DataFrame):
1055
1062
  df = spark.createDataFrame(source)
1056
1063
  elif isinstance(source, pyspark.sql.DataFrame):
@@ -1080,6 +1087,12 @@ def _ingest_with_spark(
1080
1087
  target = get_target_driver(target, featureset)
1081
1088
  target.set_resource(featureset)
1082
1089
  if featureset.spec.passthrough and target.is_offline:
1090
+ check_special_columns_exists(
1091
+ spark_df=df,
1092
+ entities=featureset.spec.entities,
1093
+ timestamp_key=timestamp_key,
1094
+ label_column=featureset.spec.label_column,
1095
+ )
1083
1096
  continue
1084
1097
  spark_options = target.get_spark_options(
1085
1098
  key_columns, timestamp_key, overwrite
@@ -1090,6 +1103,17 @@ def _ingest_with_spark(
1090
1103
  df_to_write, key_columns, timestamp_key, spark_options
1091
1104
  )
1092
1105
  write_format = spark_options.pop("format", None)
1106
+ # We can get to this point if the column exists in different letter cases,
1107
+ # so PySpark will be able to read it, but we still have to raise an exception for it.
1108
+
1109
+ # This check is here and not in to_spark_df because in spark_merger we can have a target
1110
+ # that has different letter cases than the source, like in SnowflakeTarget.
1111
+ check_special_columns_exists(
1112
+ spark_df=df_to_write,
1113
+ entities=featureset.spec.entities,
1114
+ timestamp_key=timestamp_key,
1115
+ label_column=featureset.spec.label_column,
1116
+ )
1093
1117
  if overwrite:
1094
1118
  write_spark_dataframe_with_options(
1095
1119
  spark_options, df_to_write, "overwrite", write_format=write_format
@@ -37,17 +37,12 @@ def parse_feature_string(feature):
37
37
  raise mlrun.errors.MLRunInvalidArgumentError(
38
38
  f"feature {feature} must be {expected_message}"
39
39
  )
40
- splitted = feature.split(feature_separator)
41
- if len(splitted) > 2:
42
- raise mlrun.errors.MLRunInvalidArgumentError(
43
- f"feature {feature} must be {expected_message}, cannot have more than one '.'"
44
- )
45
- feature_set = splitted[0]
46
- feature_name = splitted[1]
47
- splitted = feature_name.split(" as ")
48
- if len(splitted) > 1:
49
- return feature_set.strip(), splitted[0].strip(), splitted[1].strip()
50
- return feature_set.strip(), feature_name.strip(), None
40
+ feature_set, feature_name = feature.rsplit(feature_separator, 1)
41
+ feature_set = feature_set.strip()
42
+ split_result = feature_name.split(" as ", 1)
43
+ feature_name = split_result[0].strip()
44
+ alias = split_result[1].strip() if len(split_result) > 1 else None
45
+ return feature_set, feature_name, alias
51
46
 
52
47
 
53
48
  def parse_project_name_from_feature_string(feature):
@@ -1086,7 +1086,9 @@ class OfflineVectorResponse:
1086
1086
  def to_dataframe(self, to_pandas=True):
1087
1087
  """return result as dataframe"""
1088
1088
  if self.status != "completed":
1089
- raise mlrun.errors.MLRunTaskNotReady("feature vector dataset is not ready")
1089
+ raise mlrun.errors.MLRunTaskNotReadyError(
1090
+ "feature vector dataset is not ready"
1091
+ )
1090
1092
  return self._merger.get_df(to_pandas=to_pandas)
1091
1093
 
1092
1094
  def to_parquet(self, target_path, **kw):
@@ -156,7 +156,9 @@ class RemoteVectorResponse:
156
156
 
157
157
  def _is_ready(self):
158
158
  if self.status != "completed":
159
- raise mlrun.errors.MLRunTaskNotReady("feature vector dataset is not ready")
159
+ raise mlrun.errors.MLRunTaskNotReadyError(
160
+ "feature vector dataset is not ready"
161
+ )
160
162
  self.vector.reload()
161
163
 
162
164
  def to_dataframe(self, columns=None, df_module=None, **kwargs):
@@ -181,6 +183,7 @@ class RemoteVectorResponse:
181
183
  file_format = kwargs.get("format")
182
184
  if not file_format:
183
185
  file_format = self.run.status.results["target"]["kind"]
186
+
184
187
  df = mlrun.get_dataitem(self.target_uri).as_df(
185
188
  columns=columns, df_module=df_module, format=file_format, **kwargs
186
189
  )