mlrun 1.7.1rc10__py3-none-any.whl → 1.8.0rc11__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 (259) hide show
  1. mlrun/__init__.py +23 -21
  2. mlrun/__main__.py +3 -3
  3. mlrun/alerts/alert.py +148 -14
  4. mlrun/artifacts/__init__.py +2 -3
  5. mlrun/artifacts/base.py +55 -12
  6. mlrun/artifacts/dataset.py +16 -16
  7. mlrun/artifacts/document.py +378 -0
  8. mlrun/artifacts/manager.py +26 -17
  9. mlrun/artifacts/model.py +66 -53
  10. mlrun/common/constants.py +8 -0
  11. mlrun/common/formatters/__init__.py +1 -0
  12. mlrun/common/formatters/feature_set.py +1 -0
  13. mlrun/common/formatters/function.py +1 -0
  14. mlrun/{model_monitoring/db/stores/base/__init__.py → common/formatters/model_endpoint.py} +16 -1
  15. mlrun/common/formatters/pipeline.py +1 -2
  16. mlrun/common/formatters/project.py +9 -0
  17. mlrun/common/model_monitoring/__init__.py +0 -5
  18. mlrun/common/model_monitoring/helpers.py +1 -29
  19. mlrun/common/runtimes/constants.py +1 -2
  20. mlrun/common/schemas/__init__.py +6 -2
  21. mlrun/common/schemas/alert.py +111 -19
  22. mlrun/common/schemas/api_gateway.py +3 -3
  23. mlrun/common/schemas/artifact.py +11 -7
  24. mlrun/common/schemas/auth.py +6 -4
  25. mlrun/common/schemas/background_task.py +7 -7
  26. mlrun/common/schemas/client_spec.py +2 -3
  27. mlrun/common/schemas/clusterization_spec.py +2 -2
  28. mlrun/common/schemas/common.py +53 -3
  29. mlrun/common/schemas/constants.py +15 -0
  30. mlrun/common/schemas/datastore_profile.py +1 -1
  31. mlrun/common/schemas/feature_store.py +9 -9
  32. mlrun/common/schemas/frontend_spec.py +4 -4
  33. mlrun/common/schemas/function.py +10 -10
  34. mlrun/common/schemas/hub.py +1 -1
  35. mlrun/common/schemas/k8s.py +3 -3
  36. mlrun/common/schemas/memory_reports.py +3 -3
  37. mlrun/common/schemas/model_monitoring/__init__.py +2 -1
  38. mlrun/common/schemas/model_monitoring/constants.py +67 -14
  39. mlrun/common/schemas/model_monitoring/grafana.py +1 -1
  40. mlrun/common/schemas/model_monitoring/model_endpoints.py +92 -147
  41. mlrun/common/schemas/notification.py +24 -3
  42. mlrun/common/schemas/object.py +1 -1
  43. mlrun/common/schemas/pagination.py +4 -4
  44. mlrun/common/schemas/partition.py +137 -0
  45. mlrun/common/schemas/pipeline.py +2 -2
  46. mlrun/common/schemas/project.py +25 -17
  47. mlrun/common/schemas/runs.py +2 -2
  48. mlrun/common/schemas/runtime_resource.py +5 -5
  49. mlrun/common/schemas/schedule.py +1 -1
  50. mlrun/common/schemas/secret.py +1 -1
  51. mlrun/common/schemas/tag.py +3 -3
  52. mlrun/common/schemas/workflow.py +5 -5
  53. mlrun/config.py +68 -10
  54. mlrun/data_types/__init__.py +0 -2
  55. mlrun/data_types/data_types.py +1 -0
  56. mlrun/data_types/infer.py +3 -1
  57. mlrun/data_types/spark.py +5 -3
  58. mlrun/data_types/to_pandas.py +11 -2
  59. mlrun/datastore/__init__.py +2 -2
  60. mlrun/datastore/alibaba_oss.py +4 -1
  61. mlrun/datastore/azure_blob.py +4 -1
  62. mlrun/datastore/base.py +12 -4
  63. mlrun/datastore/datastore.py +9 -3
  64. mlrun/datastore/datastore_profile.py +79 -20
  65. mlrun/datastore/dbfs_store.py +4 -1
  66. mlrun/datastore/filestore.py +4 -1
  67. mlrun/datastore/google_cloud_storage.py +4 -1
  68. mlrun/datastore/hdfs.py +4 -1
  69. mlrun/datastore/inmem.py +4 -1
  70. mlrun/datastore/redis.py +4 -1
  71. mlrun/datastore/s3.py +4 -1
  72. mlrun/datastore/sources.py +52 -51
  73. mlrun/datastore/store_resources.py +7 -4
  74. mlrun/datastore/targets.py +23 -22
  75. mlrun/datastore/utils.py +2 -2
  76. mlrun/datastore/v3io.py +4 -1
  77. mlrun/datastore/vectorstore.py +229 -0
  78. mlrun/datastore/wasbfs/fs.py +13 -12
  79. mlrun/db/base.py +213 -83
  80. mlrun/db/factory.py +0 -3
  81. mlrun/db/httpdb.py +1265 -387
  82. mlrun/db/nopdb.py +205 -74
  83. mlrun/errors.py +2 -2
  84. mlrun/execution.py +136 -50
  85. mlrun/feature_store/__init__.py +0 -2
  86. mlrun/feature_store/api.py +41 -40
  87. mlrun/feature_store/common.py +9 -9
  88. mlrun/feature_store/feature_set.py +20 -18
  89. mlrun/feature_store/feature_vector.py +27 -24
  90. mlrun/feature_store/retrieval/base.py +14 -9
  91. mlrun/feature_store/retrieval/job.py +2 -1
  92. mlrun/feature_store/steps.py +2 -2
  93. mlrun/features.py +30 -13
  94. mlrun/frameworks/__init__.py +1 -2
  95. mlrun/frameworks/_common/__init__.py +1 -2
  96. mlrun/frameworks/_common/artifacts_library.py +2 -2
  97. mlrun/frameworks/_common/mlrun_interface.py +10 -6
  98. mlrun/frameworks/_common/model_handler.py +29 -27
  99. mlrun/frameworks/_common/producer.py +3 -1
  100. mlrun/frameworks/_dl_common/__init__.py +1 -2
  101. mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
  102. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
  103. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
  104. mlrun/frameworks/_ml_common/__init__.py +1 -2
  105. mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
  106. mlrun/frameworks/_ml_common/model_handler.py +21 -21
  107. mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
  108. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
  109. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  110. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  111. mlrun/frameworks/auto_mlrun/__init__.py +1 -2
  112. mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
  113. mlrun/frameworks/huggingface/__init__.py +1 -2
  114. mlrun/frameworks/huggingface/model_server.py +9 -9
  115. mlrun/frameworks/lgbm/__init__.py +47 -44
  116. mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
  117. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
  118. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
  119. mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
  120. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
  121. mlrun/frameworks/lgbm/model_handler.py +15 -11
  122. mlrun/frameworks/lgbm/model_server.py +11 -7
  123. mlrun/frameworks/lgbm/utils.py +2 -2
  124. mlrun/frameworks/onnx/__init__.py +1 -2
  125. mlrun/frameworks/onnx/dataset.py +3 -3
  126. mlrun/frameworks/onnx/mlrun_interface.py +2 -2
  127. mlrun/frameworks/onnx/model_handler.py +7 -5
  128. mlrun/frameworks/onnx/model_server.py +8 -6
  129. mlrun/frameworks/parallel_coordinates.py +11 -11
  130. mlrun/frameworks/pytorch/__init__.py +22 -23
  131. mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
  132. mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
  133. mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
  134. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
  135. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
  136. mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
  137. mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
  138. mlrun/frameworks/pytorch/model_handler.py +21 -17
  139. mlrun/frameworks/pytorch/model_server.py +13 -9
  140. mlrun/frameworks/sklearn/__init__.py +19 -18
  141. mlrun/frameworks/sklearn/estimator.py +2 -2
  142. mlrun/frameworks/sklearn/metric.py +3 -3
  143. mlrun/frameworks/sklearn/metrics_library.py +8 -6
  144. mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
  145. mlrun/frameworks/sklearn/model_handler.py +4 -3
  146. mlrun/frameworks/tf_keras/__init__.py +11 -12
  147. mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
  148. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
  149. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
  150. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
  151. mlrun/frameworks/tf_keras/model_handler.py +17 -13
  152. mlrun/frameworks/tf_keras/model_server.py +12 -8
  153. mlrun/frameworks/xgboost/__init__.py +19 -18
  154. mlrun/frameworks/xgboost/model_handler.py +13 -9
  155. mlrun/launcher/base.py +3 -4
  156. mlrun/launcher/local.py +1 -1
  157. mlrun/launcher/remote.py +1 -1
  158. mlrun/lists.py +4 -3
  159. mlrun/model.py +117 -46
  160. mlrun/model_monitoring/__init__.py +4 -4
  161. mlrun/model_monitoring/api.py +72 -59
  162. mlrun/model_monitoring/applications/_application_steps.py +17 -17
  163. mlrun/model_monitoring/applications/base.py +165 -6
  164. mlrun/model_monitoring/applications/context.py +88 -37
  165. mlrun/model_monitoring/applications/evidently_base.py +0 -1
  166. mlrun/model_monitoring/applications/histogram_data_drift.py +43 -21
  167. mlrun/model_monitoring/applications/results.py +55 -3
  168. mlrun/model_monitoring/controller.py +207 -239
  169. mlrun/model_monitoring/db/__init__.py +0 -2
  170. mlrun/model_monitoring/db/_schedules.py +156 -0
  171. mlrun/model_monitoring/db/_stats.py +189 -0
  172. mlrun/model_monitoring/db/tsdb/base.py +78 -25
  173. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +61 -6
  174. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
  175. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +255 -29
  176. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
  177. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +78 -17
  178. mlrun/model_monitoring/helpers.py +151 -49
  179. mlrun/model_monitoring/stream_processing.py +99 -283
  180. mlrun/model_monitoring/tracking_policy.py +10 -3
  181. mlrun/model_monitoring/writer.py +48 -36
  182. mlrun/package/__init__.py +3 -6
  183. mlrun/package/context_handler.py +1 -1
  184. mlrun/package/packager.py +12 -9
  185. mlrun/package/packagers/__init__.py +0 -2
  186. mlrun/package/packagers/default_packager.py +14 -11
  187. mlrun/package/packagers/numpy_packagers.py +16 -7
  188. mlrun/package/packagers/pandas_packagers.py +18 -18
  189. mlrun/package/packagers/python_standard_library_packagers.py +25 -11
  190. mlrun/package/packagers_manager.py +31 -14
  191. mlrun/package/utils/__init__.py +0 -3
  192. mlrun/package/utils/_pickler.py +6 -6
  193. mlrun/platforms/__init__.py +47 -16
  194. mlrun/platforms/iguazio.py +4 -1
  195. mlrun/projects/operations.py +27 -27
  196. mlrun/projects/pipelines.py +71 -36
  197. mlrun/projects/project.py +890 -220
  198. mlrun/run.py +53 -10
  199. mlrun/runtimes/__init__.py +1 -3
  200. mlrun/runtimes/base.py +15 -11
  201. mlrun/runtimes/daskjob.py +9 -9
  202. mlrun/runtimes/generators.py +2 -1
  203. mlrun/runtimes/kubejob.py +4 -5
  204. mlrun/runtimes/mounts.py +572 -0
  205. mlrun/runtimes/mpijob/__init__.py +0 -2
  206. mlrun/runtimes/mpijob/abstract.py +7 -6
  207. mlrun/runtimes/nuclio/api_gateway.py +7 -7
  208. mlrun/runtimes/nuclio/application/application.py +11 -11
  209. mlrun/runtimes/nuclio/function.py +19 -17
  210. mlrun/runtimes/nuclio/serving.py +18 -13
  211. mlrun/runtimes/pod.py +154 -45
  212. mlrun/runtimes/remotesparkjob.py +3 -2
  213. mlrun/runtimes/sparkjob/__init__.py +0 -2
  214. mlrun/runtimes/sparkjob/spark3job.py +21 -11
  215. mlrun/runtimes/utils.py +6 -5
  216. mlrun/serving/merger.py +6 -4
  217. mlrun/serving/remote.py +18 -17
  218. mlrun/serving/routers.py +185 -172
  219. mlrun/serving/server.py +7 -1
  220. mlrun/serving/states.py +97 -78
  221. mlrun/serving/utils.py +13 -2
  222. mlrun/serving/v1_serving.py +3 -2
  223. mlrun/serving/v2_serving.py +105 -72
  224. mlrun/track/__init__.py +1 -1
  225. mlrun/track/tracker.py +2 -2
  226. mlrun/track/trackers/mlflow_tracker.py +6 -5
  227. mlrun/utils/async_http.py +1 -1
  228. mlrun/utils/clones.py +1 -1
  229. mlrun/utils/helpers.py +63 -19
  230. mlrun/utils/logger.py +106 -4
  231. mlrun/utils/notifications/notification/__init__.py +22 -19
  232. mlrun/utils/notifications/notification/base.py +33 -14
  233. mlrun/utils/notifications/notification/console.py +6 -6
  234. mlrun/utils/notifications/notification/git.py +11 -11
  235. mlrun/utils/notifications/notification/ipython.py +10 -9
  236. mlrun/utils/notifications/notification/mail.py +176 -0
  237. mlrun/utils/notifications/notification/slack.py +6 -6
  238. mlrun/utils/notifications/notification/webhook.py +6 -6
  239. mlrun/utils/notifications/notification_pusher.py +86 -44
  240. mlrun/utils/regex.py +11 -2
  241. mlrun/utils/version/version.json +2 -2
  242. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc11.dist-info}/METADATA +29 -24
  243. mlrun-1.8.0rc11.dist-info/RECORD +347 -0
  244. mlrun/model_monitoring/db/stores/__init__.py +0 -136
  245. mlrun/model_monitoring/db/stores/base/store.py +0 -213
  246. mlrun/model_monitoring/db/stores/sqldb/__init__.py +0 -13
  247. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -71
  248. mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -190
  249. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -103
  250. mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -40
  251. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -659
  252. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +0 -13
  253. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -726
  254. mlrun/model_monitoring/model_endpoint.py +0 -118
  255. mlrun-1.7.1rc10.dist-info/RECORD +0 -351
  256. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc11.dist-info}/LICENSE +0 -0
  257. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc11.dist-info}/WHEEL +0 -0
  258. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc11.dist-info}/entry_points.txt +0 -0
  259. {mlrun-1.7.1rc10.dist-info → mlrun-1.8.0rc11.dist-info}/top_level.txt +0 -0
mlrun/db/httpdb.py CHANGED
@@ -22,19 +22,21 @@ import warnings
22
22
  from copy import deepcopy
23
23
  from datetime import datetime, timedelta
24
24
  from os import path, remove
25
- from typing import Optional, Union
25
+ from typing import Literal, Optional, Union
26
26
  from urllib.parse import urlparse
27
27
 
28
+ import pydantic.v1
28
29
  import requests
29
30
  import semver
30
- from mlrun_pipelines.utils import compile_pipeline
31
+ from pydantic.v1 import parse_obj_as
31
32
 
32
33
  import mlrun
34
+ import mlrun.common.constants
33
35
  import mlrun.common.formatters
34
36
  import mlrun.common.runtimes
35
37
  import mlrun.common.schemas
38
+ import mlrun.common.schemas.model_monitoring.model_endpoints as mm_endpoints
36
39
  import mlrun.common.types
37
- import mlrun.model_monitoring.model_endpoint
38
40
  import mlrun.platforms
39
41
  import mlrun.projects
40
42
  import mlrun.runtimes.nuclio.api_gateway
@@ -43,8 +45,10 @@ import mlrun.utils
43
45
  from mlrun.alerts.alert import AlertConfig
44
46
  from mlrun.db.auth_utils import OAuthClientIDTokenProvider, StaticTokenProvider
45
47
  from mlrun.errors import MLRunInvalidArgumentError, err_to_str
48
+ from mlrun_pipelines.utils import compile_pipeline
46
49
 
47
50
  from ..artifacts import Artifact
51
+ from ..common.schemas import AlertActivations
48
52
  from ..config import config
49
53
  from ..datastore.datastore_profile import DatastoreProfile2Json
50
54
  from ..feature_store import FeatureSet, FeatureVector
@@ -169,7 +173,7 @@ class HTTPRunDB(RunDBInterface):
169
173
  return f"{cls}({self.base_url!r})"
170
174
 
171
175
  @staticmethod
172
- def get_api_path_prefix(version: str = None) -> str:
176
+ def get_api_path_prefix(version: Optional[str] = None) -> str:
173
177
  """
174
178
  :param version: API version to use, None (the default) will mean to use the default value from mlrun.config,
175
179
  for un-versioned api set an empty string.
@@ -182,7 +186,7 @@ class HTTPRunDB(RunDBInterface):
182
186
  )
183
187
  return api_version_path
184
188
 
185
- def get_base_api_url(self, path: str, version: str = None) -> str:
189
+ def get_base_api_url(self, path: str, version: Optional[str] = None) -> str:
186
190
  path_prefix = self.get_api_path_prefix(version)
187
191
  url = f"{self.base_url}/{path_prefix}/{path}"
188
192
  return url
@@ -310,9 +314,26 @@ class HTTPRunDB(RunDBInterface):
310
314
  headers=None,
311
315
  timeout=45,
312
316
  version=None,
317
+ return_all=False,
313
318
  ) -> typing.Generator[requests.Response, None, None]:
314
319
  """
315
- Calls the api with pagination, yielding each page of the response
320
+ Calls the API with pagination and yields each page of the response.
321
+
322
+ Depending on the `return_all` parameter:
323
+ - If `return_all` is `True`, fetches and yields all pages of results.
324
+ - If `return_all` is False, only a single page of results is fetched and yielded.
325
+
326
+ :param method: The HTTP method (GET, POST, etc.).
327
+ :param path: The API endpoint path.
328
+ :param error: Error message used for debugging if the request fails.
329
+ :param params: The parameters to pass for the API request, including filters.
330
+ :param body: The body of the request.
331
+ :param json: The JSON payload for the request.
332
+ :param headers: Custom headers for the request.
333
+ :param timeout: Timeout for the request.
334
+ :param version: API version, optional.
335
+ :param return_all: If `True`, fetches all pages and returns them in one shot. If `False`, returns only
336
+ the requested page or the next page.
316
337
  """
317
338
 
318
339
  def _api_call(_params):
@@ -328,38 +349,50 @@ class HTTPRunDB(RunDBInterface):
328
349
  version=version,
329
350
  )
330
351
 
331
- first_page_params = deepcopy(params) or {}
332
- first_page_params["page"] = 1
333
- first_page_params["page-size"] = config.httpdb.pagination.default_page_size
334
- response = _api_call(first_page_params)
335
- page_token = response.json().get("pagination", {}).get("page-token")
336
- if not page_token:
337
- yield response
338
- return
352
+ page_params = deepcopy(params) or {}
353
+
354
+ if page_params.get("page-token") is None and page_params.get("page") is None:
355
+ page_params["page"] = 1
356
+
357
+ if page_params.get("page-size") is None:
358
+ page_params["page-size"] = config.httpdb.pagination.default_page_size
359
+
360
+ response = _api_call(page_params)
339
361
 
340
- params_with_page_token = deepcopy(params) or {}
341
- params_with_page_token["page-token"] = page_token
342
- while page_token:
343
- yield response
344
- try:
345
- response = _api_call(params_with_page_token)
346
- except mlrun.errors.MLRunNotFoundError:
347
- # pagination token expired
348
- break
362
+ # Yield only a single page of results
363
+ yield response
349
364
 
365
+ if return_all:
350
366
  page_token = response.json().get("pagination", {}).get("page-token", None)
351
367
 
368
+ while page_token:
369
+ try:
370
+ # Use the page token to get the next page.
371
+ # No need to supply any other parameters as the token informs the pagination cache
372
+ # which parameters to use.
373
+ response = _api_call({"page-token": page_token})
374
+ except mlrun.errors.MLRunNotFoundError:
375
+ # pagination token expired, we've reached the last page
376
+ break
377
+
378
+ yield response
379
+ page_token = (
380
+ response.json().get("pagination", {}).get("page-token", None)
381
+ )
382
+
352
383
  @staticmethod
353
384
  def process_paginated_responses(
354
385
  responses: typing.Generator[requests.Response, None, None], key: str = "data"
355
- ) -> list[typing.Any]:
386
+ ) -> tuple[list[typing.Any], Optional[str]]:
356
387
  """
357
388
  Processes the paginated responses and returns the combined data
358
389
  """
359
390
  data = []
391
+ page_token = None
360
392
  for response in responses:
393
+ page_token = response.json().get("pagination", {}).get("page-token", None)
361
394
  data.extend(response.json().get(key, []))
362
- return data
395
+ return data, page_token
363
396
 
364
397
  def _init_session(self, retry_on_post: bool = False):
365
398
  return mlrun.utils.HTTPSessionWithRetry(
@@ -525,10 +558,6 @@ class HTTPRunDB(RunDBInterface):
525
558
  server_cfg.get("external_platform_tracking")
526
559
  or config.external_platform_tracking
527
560
  )
528
- config.model_endpoint_monitoring.endpoint_store_connection = (
529
- server_cfg.get("model_endpoint_monitoring_endpoint_store_connection")
530
- or config.model_endpoint_monitoring.endpoint_store_connection
531
- )
532
561
  config.model_endpoint_monitoring.tsdb_connection = (
533
562
  server_cfg.get("model_monitoring_tsdb_connection")
534
563
  or config.model_endpoint_monitoring.tsdb_connection
@@ -768,7 +797,7 @@ class HTTPRunDB(RunDBInterface):
768
797
  name: Optional[str] = None,
769
798
  uid: Optional[Union[str, list[str]]] = None,
770
799
  project: Optional[str] = None,
771
- labels: Optional[Union[str, list[str]]] = None,
800
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
772
801
  state: Optional[
773
802
  mlrun.common.runtimes.constants.RunStates
774
803
  ] = None, # Backward compatibility
@@ -807,9 +836,13 @@ class HTTPRunDB(RunDBInterface):
807
836
  :param name: Name of the run to retrieve.
808
837
  :param uid: Unique ID of the run, or a list of run UIDs.
809
838
  :param project: Project that the runs belongs to.
810
- :param labels: A list of labels to filter by. Label filters work by either filtering a specific value
811
- of a label (i.e. list("key=value")) or by looking for the existence of a given
812
- key (i.e. "key").
839
+ :param labels: Filter runs by label key-value pairs or key existence. This can be provided as:
840
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
841
+ or `{"label": None}` to check for key existence.
842
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
843
+ or just `"label"` for key existence.
844
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
845
+ the specified key-value pairs or key existence.
813
846
  :param state: Deprecated - List only runs whose state is specified (will be removed in 1.9.0)
814
847
  :param states: List only runs whose state is one of the provided states.
815
848
  :param sort: Whether to sort the result according to their start time. Otherwise, results will be
@@ -821,8 +854,8 @@ class HTTPRunDB(RunDBInterface):
821
854
  :param last_update_time_from: Filter by run last update time in ``(last_update_time_from,
822
855
  last_update_time_to)``.
823
856
  :param last_update_time_to: Filter by run last update time in ``(last_update_time_from, last_update_time_to)``.
824
- :param partition_by: Field to group results by. Only allowed value is `name`. When `partition_by` is specified,
825
- the `partition_sort_by` parameter must be provided as well.
857
+ :param partition_by: Field to group results by. When `partition_by` is specified, the `partition_sort_by`
858
+ parameter must be provided as well.
826
859
  :param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
827
860
  to return per group. Default value is 1.
828
861
  :param partition_sort_by: What field to sort the results by, within each partition defined by `partition_by`.
@@ -832,81 +865,95 @@ class HTTPRunDB(RunDBInterface):
832
865
  limit.
833
866
  :param with_notifications: Return runs with notifications, and join them to the response. Default is `False`.
834
867
  """
868
+ runs, _ = self._list_runs(
869
+ name=name,
870
+ uid=uid,
871
+ project=project,
872
+ labels=labels,
873
+ state=state,
874
+ states=states,
875
+ sort=sort,
876
+ last=last,
877
+ iter=iter,
878
+ start_time_from=start_time_from,
879
+ start_time_to=start_time_to,
880
+ last_update_time_from=last_update_time_from,
881
+ last_update_time_to=last_update_time_to,
882
+ partition_by=partition_by,
883
+ rows_per_partition=rows_per_partition,
884
+ partition_sort_by=partition_sort_by,
885
+ partition_order=partition_order,
886
+ max_partitions=max_partitions,
887
+ with_notifications=with_notifications,
888
+ return_all=True,
889
+ )
890
+ return runs
835
891
 
836
- project = project or config.default_project
837
- if with_notifications:
838
- logger.warning(
839
- "Local run notifications are not persisted in the DB, therefore local runs will not be returned when "
840
- "using the `with_notifications` flag."
841
- )
842
-
843
- if last:
844
- # TODO: Remove this in 1.8.0
845
- warnings.warn(
846
- "'last' is deprecated and will be removed in 1.8.0.",
847
- FutureWarning,
848
- )
892
+ def paginated_list_runs(
893
+ self,
894
+ *args,
895
+ page: Optional[int] = None,
896
+ page_size: Optional[int] = None,
897
+ page_token: Optional[str] = None,
898
+ **kwargs,
899
+ ) -> tuple[RunList, Optional[str]]:
900
+ """List runs with support for pagination and various filtering options.
849
901
 
850
- if state:
851
- # TODO: Remove this in 1.9.0
852
- warnings.warn(
853
- "'state' is deprecated and will be removed in 1.9.0. Use 'states' instead.",
854
- FutureWarning,
855
- )
902
+ This method retrieves a paginated list of runs based on the specified filter parameters.
903
+ Pagination is controlled using the `page`, `page_size`, and `page_token` parameters. The method
904
+ will return a list of runs that match the filtering criteria provided.
856
905
 
857
- if (
858
- not name
859
- and not uid
860
- and not labels
861
- and not state
862
- and not states
863
- and not last
864
- and not start_time_from
865
- and not start_time_to
866
- and not last_update_time_from
867
- and not last_update_time_to
868
- and not partition_by
869
- and not partition_sort_by
870
- and not iter
871
- ):
872
- # default to last week on no filter
873
- start_time_from = datetime.now() - timedelta(days=7)
874
- partition_by = mlrun.common.schemas.RunPartitionByField.project_and_name
875
- partition_sort_by = mlrun.common.schemas.SortField.updated
906
+ For detailed information about the parameters, refer to the list_runs method:
907
+ See :py:func:`~list_runs` for more details.
876
908
 
877
- params = {
878
- "name": name,
879
- "uid": uid,
880
- "label": labels or [],
881
- "state": mlrun.utils.helpers.as_list(state)
882
- if state is not None
883
- else states or None,
884
- "sort": bool2str(sort),
885
- "iter": bool2str(iter),
886
- "start_time_from": datetime_to_iso(start_time_from),
887
- "start_time_to": datetime_to_iso(start_time_to),
888
- "last_update_time_from": datetime_to_iso(last_update_time_from),
889
- "last_update_time_to": datetime_to_iso(last_update_time_to),
890
- "with-notifications": with_notifications,
891
- }
909
+ Examples::
892
910
 
893
- if partition_by:
894
- params.update(
895
- self._generate_partition_by_params(
896
- mlrun.common.schemas.RunPartitionByField,
897
- partition_by,
898
- rows_per_partition,
899
- partition_sort_by,
900
- partition_order,
901
- max_partitions,
911
+ # Fetch first page of runs with page size of 5
912
+ runs, token = db.paginated_list_runs(project="my-project", page_size=5)
913
+ # Fetch next page using the pagination token from the previous response
914
+ runs, token = db.paginated_list_runs(project="my-project", page_token=token)
915
+ # Fetch runs for a specific page (e.g., page 3)
916
+ runs, token = db.paginated_list_runs(project="my-project", page=3, page_size=5)
917
+
918
+ # Automatically iterate over all pages without explicitly specifying the page number
919
+ runs = []
920
+ token = None
921
+ while True:
922
+ page_runs, token = db.paginated_list_runs(
923
+ project="my-project", page_token=token, page_size=5
902
924
  )
903
- )
904
- error = "list runs"
905
- _path = self._path_of("runs", project)
906
- responses = self.paginated_api_call("GET", _path, error, params=params)
907
- return RunList(self.process_paginated_responses(responses, "runs"))
925
+ runs.extend(page_runs)
908
926
 
909
- def del_runs(self, name=None, project=None, labels=None, state=None, days_ago=0):
927
+ # If token is None and page_runs is empty, we've reached the end (no more runs).
928
+ # If token is None and page_runs is not empty, we've fetched the last page of runs.
929
+ if not token:
930
+ break
931
+ print(f"Total runs retrieved: {len(runs)}")
932
+
933
+ :param page: The page number to retrieve. If not provided, the next page will be retrieved.
934
+ :param page_size: The number of items per page to retrieve. Up to `page_size` responses are expected.
935
+ :param page_token: A pagination token used to retrieve the next page of results. Should not be provided
936
+ for the first request.
937
+
938
+ :returns: A tuple containing the list of runs and an optional `page_token` for pagination.
939
+ """
940
+ return self._list_runs(
941
+ *args,
942
+ page=page,
943
+ page_size=page_size,
944
+ page_token=page_token,
945
+ return_all=False,
946
+ **kwargs,
947
+ )
948
+
949
+ def del_runs(
950
+ self,
951
+ name: Optional[str] = None,
952
+ project: Optional[str] = None,
953
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
954
+ state: Optional[mlrun.common.runtimes.constants.RunStates] = None,
955
+ days_ago: int = 0,
956
+ ):
910
957
  """Delete a group of runs identified by the parameters of the function.
911
958
 
912
959
  Example::
@@ -915,16 +962,23 @@ class HTTPRunDB(RunDBInterface):
915
962
 
916
963
  :param name: Name of the task which the runs belong to.
917
964
  :param project: Project to which the runs belong.
918
- :param labels: Filter runs that are labeled using these specific label values.
965
+ :param labels: Filter runs by label key-value pairs or key existence. This can be provided as:
966
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
967
+ or `{"label": None}` to check for key existence.
968
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
969
+ or just `"label"` for key existence.
970
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
971
+ the specified key-value pairs or key existence.
919
972
  :param state: Filter only runs which are in this state.
920
973
  :param days_ago: Filter runs whose start time is newer than this parameter.
921
974
  """
922
975
 
923
976
  project = project or config.default_project
977
+ labels = self._parse_labels(labels)
924
978
  params = {
925
979
  "name": name,
926
980
  "project": project,
927
- "label": labels or [],
981
+ "label": labels,
928
982
  "state": state,
929
983
  "days_ago": str(days_ago),
930
984
  }
@@ -942,7 +996,7 @@ class HTTPRunDB(RunDBInterface):
942
996
  tag=None,
943
997
  project="",
944
998
  tree=None,
945
- ):
999
+ ) -> dict[str, str]:
946
1000
  """Store an artifact in the DB.
947
1001
 
948
1002
  :param key: Identifying key of the artifact.
@@ -954,6 +1008,7 @@ class HTTPRunDB(RunDBInterface):
954
1008
  :param tag: Tag of the artifact.
955
1009
  :param project: Project that the artifact belongs to.
956
1010
  :param tree: The tree (producer id) which generated this artifact.
1011
+ :returns: The stored artifact dictionary.
957
1012
  """
958
1013
  if uid:
959
1014
  warnings.warn(
@@ -978,9 +1033,10 @@ class HTTPRunDB(RunDBInterface):
978
1033
  params["tree"] = tree
979
1034
 
980
1035
  body = _as_json(artifact)
981
- self.api_call(
1036
+ response = self.api_call(
982
1037
  "PUT", endpoint_path, error, body=body, params=params, version="v2"
983
1038
  )
1039
+ return response.json()
984
1040
 
985
1041
  def read_artifact(
986
1042
  self,
@@ -1028,7 +1084,7 @@ class HTTPRunDB(RunDBInterface):
1028
1084
  deletion_strategy: mlrun.common.schemas.artifact.ArtifactsDeletionStrategies = (
1029
1085
  mlrun.common.schemas.artifact.ArtifactsDeletionStrategies.metadata_only
1030
1086
  ),
1031
- secrets: dict = None,
1087
+ secrets: Optional[dict] = None,
1032
1088
  iter=None,
1033
1089
  ):
1034
1090
  """Delete an artifact.
@@ -1063,29 +1119,39 @@ class HTTPRunDB(RunDBInterface):
1063
1119
 
1064
1120
  def list_artifacts(
1065
1121
  self,
1066
- name=None,
1067
- project=None,
1068
- tag=None,
1069
- labels: Optional[Union[dict[str, str], list[str]]] = None,
1122
+ name: Optional[str] = None,
1123
+ project: Optional[str] = None,
1124
+ tag: Optional[str] = None,
1125
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
1070
1126
  since: Optional[datetime] = None,
1071
1127
  until: Optional[datetime] = None,
1072
- iter: int = None,
1128
+ iter: Optional[int] = None,
1073
1129
  best_iteration: bool = False,
1074
- kind: str = None,
1130
+ kind: Optional[str] = None,
1075
1131
  category: Union[str, mlrun.common.schemas.ArtifactCategories] = None,
1076
- tree: str = None,
1077
- producer_uri: str = None,
1132
+ tree: Optional[str] = None,
1133
+ producer_uri: Optional[str] = None,
1078
1134
  format_: Optional[
1079
1135
  mlrun.common.formatters.ArtifactFormat
1080
1136
  ] = mlrun.common.formatters.ArtifactFormat.full,
1081
- limit: int = None,
1137
+ limit: Optional[int] = None,
1138
+ partition_by: Optional[
1139
+ Union[mlrun.common.schemas.ArtifactPartitionByField, str]
1140
+ ] = None,
1141
+ rows_per_partition: int = 1,
1142
+ partition_sort_by: Optional[
1143
+ Union[mlrun.common.schemas.SortField, str]
1144
+ ] = mlrun.common.schemas.SortField.updated,
1145
+ partition_order: Union[
1146
+ mlrun.common.schemas.OrderType, str
1147
+ ] = mlrun.common.schemas.OrderType.desc,
1082
1148
  ) -> ArtifactList:
1083
1149
  """List artifacts filtered by various parameters.
1084
1150
 
1085
1151
  Examples::
1086
1152
 
1087
1153
  # Show latest version of all artifacts in project
1088
- latest_artifacts = db.list_artifacts("", tag="latest", project="iris")
1154
+ latest_artifacts = db.list_artifacts(tag="latest", project="iris")
1089
1155
  # check different artifact versions for a specific artifact
1090
1156
  result_versions = db.list_artifacts("results", tag="*", project="iris")
1091
1157
  # Show artifacts with label filters - both uploaded and of binary type
@@ -1098,8 +1164,13 @@ class HTTPRunDB(RunDBInterface):
1098
1164
  ``my_Name_1`` or ``surname``.
1099
1165
  :param project: Project name.
1100
1166
  :param tag: Return artifacts assigned this tag.
1101
- :param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
1102
- a list of "label=value" (match label key and value) or "label" (match just label key) strings.
1167
+ :param labels: Filter artifacts by label key-value pairs or key existence. This can be provided as:
1168
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
1169
+ or `{"label": None}` to check for key existence.
1170
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
1171
+ or just `"label"` for key existence.
1172
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
1173
+ the specified key-value pairs or key existence.
1103
1174
  :param since: Return artifacts updated after this date (as datetime object).
1104
1175
  :param until: Return artifacts updated before this date (as datetime object).
1105
1176
  :param iter: Return artifacts from a specific iteration (where ``iter=0`` means the root iteration). If
@@ -1115,38 +1186,110 @@ class HTTPRunDB(RunDBInterface):
1115
1186
  is a workflow id (artifact was created as part of a workflow).
1116
1187
  :param format_: The format in which to return the artifacts. Default is 'full'.
1117
1188
  :param limit: Maximum number of artifacts to return.
1189
+ :param partition_by: Field to group results by. When `partition_by` is specified, the `partition_sort_by`
1190
+ parameter must be provided as well.
1191
+ :param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
1192
+ to return per group. Default value is 1.
1193
+ :param partition_sort_by: What field to sort the results by, within each partition defined by `partition_by`.
1194
+ Currently the only allowed values are `created` and `updated`.
1195
+ :param partition_order: Order of sorting within partitions - `asc` or `desc`. Default is `desc`.
1118
1196
  """
1119
1197
 
1120
- project = project or config.default_project
1198
+ artifacts, _ = self._list_artifacts(
1199
+ name=name,
1200
+ project=project,
1201
+ tag=tag,
1202
+ labels=labels,
1203
+ since=since,
1204
+ until=until,
1205
+ iter=iter,
1206
+ best_iteration=best_iteration,
1207
+ kind=kind,
1208
+ category=category,
1209
+ tree=tree,
1210
+ producer_uri=producer_uri,
1211
+ format_=format_,
1212
+ limit=limit,
1213
+ partition_by=partition_by,
1214
+ rows_per_partition=rows_per_partition,
1215
+ partition_sort_by=partition_sort_by,
1216
+ partition_order=partition_order,
1217
+ return_all=True,
1218
+ )
1219
+ return artifacts
1121
1220
 
1122
- labels = labels or []
1123
- if isinstance(labels, dict):
1124
- labels = [f"{key}={value}" for key, value in labels.items()]
1221
+ def paginated_list_artifacts(
1222
+ self,
1223
+ *args,
1224
+ page: Optional[int] = None,
1225
+ page_size: Optional[int] = None,
1226
+ page_token: Optional[str] = None,
1227
+ **kwargs,
1228
+ ) -> tuple[ArtifactList, Optional[str]]:
1229
+ """List artifacts with support for pagination and various filtering options.
1125
1230
 
1126
- params = {
1127
- "name": name,
1128
- "tag": tag,
1129
- "label": labels,
1130
- "iter": iter,
1131
- "best-iteration": best_iteration,
1132
- "kind": kind,
1133
- "category": category,
1134
- "tree": tree,
1135
- "format": format_,
1136
- "producer_uri": producer_uri,
1137
- "limit": limit,
1138
- "since": datetime_to_iso(since),
1139
- "until": datetime_to_iso(until),
1140
- }
1141
- error = "list artifacts"
1142
- endpoint_path = f"projects/{project}/artifacts"
1143
- resp = self.api_call("GET", endpoint_path, error, params=params, version="v2")
1144
- values = ArtifactList(resp.json()["artifacts"])
1145
- values.tag = tag
1146
- return values
1231
+ This method retrieves a paginated list of artifacts based on the specified filter parameters.
1232
+ Pagination is controlled using the `page`, `page_size`, and `page_token` parameters. The method
1233
+ will return a list of artifacts that match the filtering criteria provided.
1234
+
1235
+ For detailed information about the parameters, refer to the list_artifacts method:
1236
+ See :py:func:`~list_artifacts` for more details.
1237
+
1238
+ Examples::
1239
+
1240
+ # Fetch first page of artifacts with page size of 5
1241
+ artifacts, token = db.paginated_list_artifacts(
1242
+ project="my-project", page_size=5
1243
+ )
1244
+ # Fetch next page using the pagination token from the previous response
1245
+ artifacts, token = db.paginated_list_artifacts(
1246
+ project="my-project", page_token=token
1247
+ )
1248
+ # Fetch artifacts for a specific page (e.g., page 3)
1249
+ artifacts, token = db.paginated_list_artifacts(
1250
+ project="my-project", page=3, page_size=5
1251
+ )
1252
+
1253
+ # Automatically iterate over all pages without explicitly specifying the page number
1254
+ artifacts = []
1255
+ token = None
1256
+ while True:
1257
+ page_artifacts, token = db.paginated_list_artifacts(
1258
+ project="my-project", page_token=token, page_size=5
1259
+ )
1260
+ artifacts.extend(page_artifacts)
1261
+
1262
+ # If token is None and page_artifacts is empty, we've reached the end (no more artifacts).
1263
+ # If token is None and page_artifacts is not empty, we've fetched the last page of artifacts.
1264
+ if not token:
1265
+ break
1266
+ print(f"Total artifacts retrieved: {len(artifacts)}")
1267
+
1268
+ :param page: The page number to retrieve. If not provided, the next page will be retrieved.
1269
+ :param page_size: The number of items per page to retrieve. Up to `page_size` responses are expected.
1270
+ :param page_token: A pagination token used to retrieve the next page of results. Should not be provided
1271
+ for the first request.
1272
+
1273
+ :returns: A tuple containing the list of artifacts and an optional `page_token` for pagination.
1274
+ """
1275
+
1276
+ return self._list_artifacts(
1277
+ *args,
1278
+ page=page,
1279
+ page_size=page_size,
1280
+ page_token=page_token,
1281
+ return_all=False,
1282
+ **kwargs,
1283
+ )
1147
1284
 
1148
1285
  def del_artifacts(
1149
- self, name=None, project=None, tag=None, labels=None, days_ago=0, tree=None
1286
+ self,
1287
+ name: Optional[str] = None,
1288
+ project: Optional[str] = None,
1289
+ tag: Optional[str] = None,
1290
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
1291
+ days_ago=0,
1292
+ tree: Optional[str] = None,
1150
1293
  ):
1151
1294
  """Delete artifacts referenced by the parameters.
1152
1295
 
@@ -1154,15 +1297,24 @@ class HTTPRunDB(RunDBInterface):
1154
1297
  :py:func:`~list_artifacts` for more details.
1155
1298
  :param project: Project that artifacts belong to.
1156
1299
  :param tag: Choose artifacts who are assigned this tag.
1157
- :param labels: Choose artifacts which are labeled.
1300
+ :param labels: Filter artifacts by label key-value pairs or key existence. This can be provided as:
1301
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
1302
+ or `{"label": None}` to check for key existence.
1303
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
1304
+ or just `"label"` for key existence.
1305
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
1306
+ the specified key-value pairs or key existence.
1158
1307
  :param days_ago: This parameter is deprecated and not used.
1308
+ :param tree: Delete artifacts filtered by tree.
1159
1309
  """
1160
1310
  project = project or config.default_project
1311
+ labels = self._parse_labels(labels)
1312
+
1161
1313
  params = {
1162
1314
  "name": name,
1163
1315
  "tag": tag,
1164
1316
  "tree": tree,
1165
- "label": labels or [],
1317
+ "label": labels,
1166
1318
  "days_ago": str(days_ago),
1167
1319
  }
1168
1320
  error = "del artifacts"
@@ -1254,30 +1406,110 @@ class HTTPRunDB(RunDBInterface):
1254
1406
  )
1255
1407
 
1256
1408
  def list_functions(
1257
- self, name=None, project=None, tag=None, labels=None, since=None, until=None
1409
+ self,
1410
+ name: Optional[str] = None,
1411
+ project: Optional[str] = None,
1412
+ tag: Optional[str] = None,
1413
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
1414
+ since: Optional[datetime] = None,
1415
+ until: Optional[datetime] = None,
1416
+ kind: Optional[str] = None,
1417
+ format_: mlrun.common.formatters.FunctionFormat = mlrun.common.formatters.FunctionFormat.full,
1258
1418
  ):
1259
1419
  """Retrieve a list of functions, filtered by specific criteria.
1260
1420
 
1261
1421
  :param name: Return only functions with a specific name.
1262
1422
  :param project: Return functions belonging to this project. If not specified, the default project is used.
1263
1423
  :param tag: Return function versions with specific tags. To return only tagged functions, set tag to ``"*"``.
1264
- :param labels: Return functions that have specific labels assigned to them.
1424
+ :param labels: Filter functions by label key-value pairs or key existence. This can be provided as:
1425
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
1426
+ or `{"label": None}` to check for key existence.
1427
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
1428
+ or just `"label"` for key existence.
1429
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
1430
+ the specified key-value pairs or key existence.
1265
1431
  :param since: Return functions updated after this date (as datetime object).
1266
1432
  :param until: Return functions updated before this date (as datetime object).
1433
+ :param kind: Return only functions of a specific kind.
1434
+ :param format_: The format in which to return the functions. Default is 'full'.
1267
1435
  :returns: List of function objects (as dictionary).
1268
1436
  """
1269
- project = project or config.default_project
1270
- params = {
1271
- "name": name,
1272
- "tag": tag,
1273
- "label": labels or [],
1274
- "since": datetime_to_iso(since),
1275
- "until": datetime_to_iso(until),
1276
- }
1277
- error = "list functions"
1278
- path = f"projects/{project}/functions"
1279
- responses = self.paginated_api_call("GET", path, error, params=params)
1280
- return self.process_paginated_responses(responses, "funcs")
1437
+ functions, _ = self._list_functions(
1438
+ name=name,
1439
+ project=project,
1440
+ tag=tag,
1441
+ kind=kind,
1442
+ labels=labels,
1443
+ format_=format_,
1444
+ since=since,
1445
+ until=until,
1446
+ return_all=True,
1447
+ )
1448
+ return functions
1449
+
1450
+ def paginated_list_functions(
1451
+ self,
1452
+ *args,
1453
+ page: Optional[int] = None,
1454
+ page_size: Optional[int] = None,
1455
+ page_token: Optional[str] = None,
1456
+ **kwargs,
1457
+ ) -> tuple[list[dict], Optional[str]]:
1458
+ """List functions with support for pagination and various filtering options.
1459
+
1460
+ This method retrieves a paginated list of functions based on the specified filter parameters.
1461
+ Pagination is controlled using the `page`, `page_size`, and `page_token` parameters. The method
1462
+ will return a list of functions that match the filtering criteria provided.
1463
+
1464
+ For detailed information about the parameters, refer to the list_functions method:
1465
+ See :py:func:`~list_functions` for more details.
1466
+
1467
+ Examples::
1468
+
1469
+ # Fetch first page of functions with page size of 5
1470
+ functions, token = db.paginated_list_functions(
1471
+ project="my-project", page_size=5
1472
+ )
1473
+ # Fetch next page using the pagination token from the previous response
1474
+ functions, token = db.paginated_list_functions(
1475
+ project="my-project", page_token=token
1476
+ )
1477
+ # Fetch functions for a specific page (e.g., page 3)
1478
+ functions, token = db.paginated_list_functions(
1479
+ project="my-project", page=3, page_size=5
1480
+ )
1481
+
1482
+ # Automatically iterate over all pages without explicitly specifying the page number
1483
+ functions = []
1484
+ token = None
1485
+ while True:
1486
+ page_functions, token = db.paginated_list_functions(
1487
+ project="my-project", page_token=token, page_size=5
1488
+ )
1489
+ functions.extend(page_functions)
1490
+
1491
+ # If token is None and page_functions is empty, we've reached the end (no more functions).
1492
+ # If token is None and page_functions is not empty, we've fetched the last page of functions.
1493
+ if not token:
1494
+ break
1495
+ print(f"Total functions retrieved: {len(functions)}")
1496
+
1497
+ :param page: The page number to retrieve. If not provided, the next page will be retrieved.
1498
+ :param page_size: The number of items per page to retrieve. Up to `page_size` responses are expected.
1499
+ :param page_token: A pagination token used to retrieve the next page of results. Should not be provided
1500
+ for the first request.
1501
+
1502
+ :returns: A tuple containing the list of functions objects (as dictionary) and an optional
1503
+ `page_token` for pagination.
1504
+ """
1505
+ return self._list_functions(
1506
+ *args,
1507
+ page=page,
1508
+ page_size=page_size,
1509
+ page_token=page_token,
1510
+ return_all=False,
1511
+ **kwargs,
1512
+ )
1281
1513
 
1282
1514
  def list_runtime_resources(
1283
1515
  self,
@@ -1352,7 +1584,7 @@ class HTTPRunDB(RunDBInterface):
1352
1584
  kind: Optional[str] = None,
1353
1585
  object_id: Optional[str] = None,
1354
1586
  force: bool = False,
1355
- grace_period: int = None,
1587
+ grace_period: Optional[int] = None,
1356
1588
  ) -> mlrun.common.schemas.GroupedByProjectRuntimeResourcesOutput:
1357
1589
  """Delete all runtime resources which are in terminal state.
1358
1590
 
@@ -1464,9 +1696,11 @@ class HTTPRunDB(RunDBInterface):
1464
1696
  def list_schedules(
1465
1697
  self,
1466
1698
  project: str,
1467
- name: str = None,
1699
+ name: Optional[str] = None,
1468
1700
  kind: mlrun.common.schemas.ScheduleKinds = None,
1469
1701
  include_last_run: bool = False,
1702
+ next_run_time_since: Optional[datetime] = None,
1703
+ next_run_time_until: Optional[datetime] = None,
1470
1704
  ) -> mlrun.common.schemas.SchedulesOutput:
1471
1705
  """Retrieve list of schedules of specific name or kind.
1472
1706
 
@@ -1475,10 +1709,18 @@ class HTTPRunDB(RunDBInterface):
1475
1709
  :param kind: Kind of schedule objects to retrieve, can be either ``job`` or ``pipeline``.
1476
1710
  :param include_last_run: Whether to return for each schedule returned also the results of the last run of
1477
1711
  that schedule.
1712
+ :param next_run_time_since: Return only schedules with next run time after this date.
1713
+ :param next_run_time_until: Return only schedules with next run time before this date.
1478
1714
  """
1479
1715
 
1480
1716
  project = project or config.default_project
1481
- params = {"kind": kind, "name": name, "include_last_run": include_last_run}
1717
+ params = {
1718
+ "kind": kind,
1719
+ "name": name,
1720
+ "include_last_run": include_last_run,
1721
+ "next_run_time_since": datetime_to_iso(next_run_time_since),
1722
+ "next_run_time_until": datetime_to_iso(next_run_time_until),
1723
+ }
1482
1724
  path = f"projects/{project}/schedules"
1483
1725
  error_message = f"Failed listing schedules for {project} ? {kind} {name}"
1484
1726
  resp = self.api_call("GET", path, error_message, params=params)
@@ -1636,6 +1878,7 @@ class HTTPRunDB(RunDBInterface):
1636
1878
  logs: bool = True,
1637
1879
  last_log_timestamp: float = 0.0,
1638
1880
  verbose: bool = False,
1881
+ events_offset: int = 0,
1639
1882
  ):
1640
1883
  """Retrieve the status of a build operation currently in progress.
1641
1884
 
@@ -1645,6 +1888,7 @@ class HTTPRunDB(RunDBInterface):
1645
1888
  :param last_log_timestamp: Last timestamp of logs that were already retrieved. Function will return only logs
1646
1889
  later than this parameter.
1647
1890
  :param verbose: Add verbose logs into the output.
1891
+ :param events_offset: Offset into the build events to retrieve events from.
1648
1892
 
1649
1893
  :returns: The following parameters:
1650
1894
 
@@ -1661,6 +1905,7 @@ class HTTPRunDB(RunDBInterface):
1661
1905
  "tag": func.metadata.tag,
1662
1906
  "logs": bool2str(logs),
1663
1907
  "offset": str(offset),
1908
+ "events_offset": str(events_offset),
1664
1909
  "last_log_timestamp": str(last_log_timestamp),
1665
1910
  "verbose": bool2str(verbose),
1666
1911
  }
@@ -1673,6 +1918,7 @@ class HTTPRunDB(RunDBInterface):
1673
1918
  logger.warning(f"failed resp, {resp.text}")
1674
1919
  raise RunDBError("bad function build response")
1675
1920
 
1921
+ deploy_status_text_kind = mlrun.common.constants.DeployStatusTextKind.logs
1676
1922
  if resp.headers:
1677
1923
  func.status.state = resp.headers.get("x-mlrun-function-status", "")
1678
1924
  last_log_timestamp = float(
@@ -1691,13 +1937,20 @@ class HTTPRunDB(RunDBInterface):
1691
1937
  if function_image:
1692
1938
  func.spec.image = function_image
1693
1939
 
1940
+ deploy_status_text_kind = resp.headers.get(
1941
+ "deploy_status_text_kind",
1942
+ mlrun.common.constants.DeployStatusTextKind.logs,
1943
+ )
1944
+
1694
1945
  text = ""
1695
1946
  if resp.content:
1696
1947
  text = resp.content.decode()
1697
- return text, last_log_timestamp
1948
+ return text, last_log_timestamp, deploy_status_text_kind
1698
1949
 
1699
1950
  def start_function(
1700
- self, func_url: str = None, function: "mlrun.runtimes.BaseRuntime" = None
1951
+ self,
1952
+ func_url: Optional[str] = None,
1953
+ function: "mlrun.runtimes.BaseRuntime" = None,
1701
1954
  ) -> mlrun.common.schemas.BackgroundTask:
1702
1955
  """Execute a function remotely, Used for ``dask`` functions.
1703
1956
 
@@ -1939,14 +2192,14 @@ class HTTPRunDB(RunDBInterface):
1939
2192
  def list_pipelines(
1940
2193
  self,
1941
2194
  project: str,
1942
- namespace: str = None,
2195
+ namespace: Optional[str] = None,
1943
2196
  sort_by: str = "",
1944
2197
  page_token: str = "",
1945
2198
  filter_: str = "",
1946
2199
  format_: Union[
1947
2200
  str, mlrun.common.formatters.PipelineFormat
1948
2201
  ] = mlrun.common.formatters.PipelineFormat.metadata_only,
1949
- page_size: int = None,
2202
+ page_size: Optional[int] = None,
1950
2203
  ) -> mlrun.common.schemas.PipelinesOutput:
1951
2204
  """Retrieve a list of KFP pipelines. This function can be invoked to get all pipelines from all projects,
1952
2205
  by specifying ``project=*``, in which case pagination can be used and the various sorting and pagination
@@ -1988,12 +2241,12 @@ class HTTPRunDB(RunDBInterface):
1988
2241
  def get_pipeline(
1989
2242
  self,
1990
2243
  run_id: str,
1991
- namespace: str = None,
2244
+ namespace: Optional[str] = None,
1992
2245
  timeout: int = 30,
1993
2246
  format_: Union[
1994
2247
  str, mlrun.common.formatters.PipelineFormat
1995
2248
  ] = mlrun.common.formatters.PipelineFormat.summary,
1996
- project: str = None,
2249
+ project: Optional[str] = None,
1997
2250
  ):
1998
2251
  """Retrieve details of a specific pipeline using its run ID (as provided when the pipeline was executed)."""
1999
2252
 
@@ -2015,6 +2268,75 @@ class HTTPRunDB(RunDBInterface):
2015
2268
 
2016
2269
  return resp.json()
2017
2270
 
2271
+ def retry_pipeline(
2272
+ self,
2273
+ run_id: str,
2274
+ namespace: Optional[str] = None,
2275
+ timeout: int = 30,
2276
+ project: Optional[str] = None,
2277
+ ):
2278
+ """
2279
+ Retry a specific pipeline run using its run ID. This function sends an API request
2280
+ to retry a pipeline run. If a project is specified, the run must belong to that
2281
+ project; otherwise, all projects are queried.
2282
+
2283
+ :param run_id: The unique ID of the pipeline run to retry.
2284
+ :param namespace: Kubernetes namespace where the pipeline is running. Optional.
2285
+ :param timeout: Timeout (in seconds) for the API call. Defaults to 30 seconds.
2286
+ :param project: Name of the MLRun project associated with the pipeline. Can be
2287
+ ``*`` to query across all projects. Optional.
2288
+
2289
+ :raises ValueError: Raised if the API response is not successful or contains an
2290
+ error.
2291
+
2292
+ :return: JSON response containing details of the retried pipeline run.
2293
+ """
2294
+
2295
+ params = {}
2296
+ if namespace:
2297
+ params["namespace"] = namespace
2298
+ project_path = project if project else "*"
2299
+
2300
+ resp_text = ""
2301
+ resp_code = None
2302
+ try:
2303
+ resp = self.api_call(
2304
+ "POST",
2305
+ f"projects/{project_path}/pipelines/{run_id}/retry",
2306
+ params=params,
2307
+ timeout=timeout,
2308
+ )
2309
+ resp_code = resp.status_code
2310
+ resp_text = resp.text
2311
+ if not resp.ok:
2312
+ raise mlrun.errors.MLRunHTTPError(
2313
+ f"Failed to retry pipeline run '{run_id}'. "
2314
+ f"HTTP {resp_code}: {resp_text}"
2315
+ )
2316
+ except Exception as exc:
2317
+ logger.error(
2318
+ "Retry pipeline API call encountered an error.",
2319
+ run_id=run_id,
2320
+ project=project_path,
2321
+ namespace=namespace,
2322
+ response_code=resp_code,
2323
+ response_text=resp_text,
2324
+ error=str(exc),
2325
+ )
2326
+ if isinstance(exc, mlrun.errors.MLRunHTTPError):
2327
+ raise exc # Re-raise known HTTP errors
2328
+ raise mlrun.errors.MLRunRuntimeError(
2329
+ f"Unexpected error while retrying pipeline run '{run_id}'."
2330
+ ) from exc
2331
+
2332
+ logger.info(
2333
+ "Successfully retried pipeline run",
2334
+ run_id=run_id,
2335
+ project=project_path,
2336
+ namespace=namespace,
2337
+ )
2338
+ return resp.json()
2339
+
2018
2340
  @staticmethod
2019
2341
  def _resolve_reference(tag, uid):
2020
2342
  if uid and tag:
@@ -2061,7 +2383,11 @@ class HTTPRunDB(RunDBInterface):
2061
2383
  return resp.json()
2062
2384
 
2063
2385
  def get_feature_set(
2064
- self, name: str, project: str = "", tag: str = None, uid: str = None
2386
+ self,
2387
+ name: str,
2388
+ project: str = "",
2389
+ tag: Optional[str] = None,
2390
+ uid: Optional[str] = None,
2065
2391
  ) -> FeatureSet:
2066
2392
  """Retrieve a ~mlrun.feature_store.FeatureSet` object. If both ``tag`` and ``uid`` are not specified, then
2067
2393
  the object tagged ``latest`` will be retrieved.
@@ -2081,11 +2407,11 @@ class HTTPRunDB(RunDBInterface):
2081
2407
 
2082
2408
  def list_features(
2083
2409
  self,
2084
- project: str,
2085
- name: str = None,
2086
- tag: str = None,
2087
- entities: list[str] = None,
2088
- labels: list[str] = None,
2410
+ project: Optional[str] = None,
2411
+ name: Optional[str] = None,
2412
+ tag: Optional[str] = None,
2413
+ entities: Optional[list[str]] = None,
2414
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2089
2415
  ) -> list[dict]:
2090
2416
  """List feature-sets which contain specific features. This function may return multiple versions of the same
2091
2417
  feature-set if a specific tag is not requested. Note that the various filters of this function actually
@@ -2096,18 +2422,25 @@ class HTTPRunDB(RunDBInterface):
2096
2422
  example, looking for ``feat`` will return features which are named ``MyFeature`` as well as ``defeat``.
2097
2423
  :param tag: Return feature-sets which contain the features looked for, and are tagged with the specific tag.
2098
2424
  :param entities: Return only feature-sets which contain an entity whose name is contained in this list.
2099
- :param labels: Return only feature-sets which are labeled as requested.
2425
+ :param labels: Filter feature-sets by label key-value pairs or key existence. This can be provided as:
2426
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2427
+ or `{"label": None}` to check for key existence.
2428
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2429
+ or just `"label"` for key existence.
2430
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2431
+ the specified key-value pairs or key existence.
2100
2432
  :returns: A list of mapping from feature to a digest of the feature-set, which contains the feature-set
2101
2433
  meta-data. Multiple entries may be returned for any specific feature due to multiple tags or versions
2102
2434
  of the feature-set.
2103
2435
  """
2104
2436
 
2105
2437
  project = project or config.default_project
2438
+ labels = self._parse_labels(labels)
2106
2439
  params = {
2107
2440
  "name": name,
2108
2441
  "tag": tag,
2109
2442
  "entity": entities or [],
2110
- "label": labels or [],
2443
+ "label": labels,
2111
2444
  }
2112
2445
 
2113
2446
  path = f"projects/{project}/features"
@@ -2118,11 +2451,11 @@ class HTTPRunDB(RunDBInterface):
2118
2451
 
2119
2452
  def list_features_v2(
2120
2453
  self,
2121
- project: str,
2122
- name: str = None,
2123
- tag: str = None,
2124
- entities: list[str] = None,
2125
- labels: list[str] = None,
2454
+ project: Optional[str] = None,
2455
+ name: Optional[str] = None,
2456
+ tag: Optional[str] = None,
2457
+ entities: Optional[list[str]] = None,
2458
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2126
2459
  ) -> dict[str, list[dict]]:
2127
2460
  """List feature-sets which contain specific features. This function may return multiple versions of the same
2128
2461
  feature-set if a specific tag is not requested. Note that the various filters of this function actually
@@ -2133,16 +2466,23 @@ class HTTPRunDB(RunDBInterface):
2133
2466
  example, looking for ``feat`` will return features which are named ``MyFeature`` as well as ``defeat``.
2134
2467
  :param tag: Return feature-sets which contain the features looked for, and are tagged with the specific tag.
2135
2468
  :param entities: Return only feature-sets which contain an entity whose name is contained in this list.
2136
- :param labels: Return only feature-sets which are labeled as requested.
2469
+ :param labels: Filter feature-sets by label key-value pairs or key existence. This can be provided as:
2470
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2471
+ or `{"label": None}` to check for key existence.
2472
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2473
+ or just `"label"` for key existence.
2474
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2475
+ the specified key-value pairs or key existence.
2137
2476
  :returns: A list of features, and a list of their corresponding feature sets.
2138
2477
  """
2139
2478
 
2140
2479
  project = project or config.default_project
2480
+ labels = self._parse_labels(labels)
2141
2481
  params = {
2142
2482
  "name": name,
2143
2483
  "tag": tag,
2144
2484
  "entity": entities or [],
2145
- "label": labels or [],
2485
+ "label": labels,
2146
2486
  }
2147
2487
 
2148
2488
  path = f"projects/{project}/features"
@@ -2153,21 +2493,34 @@ class HTTPRunDB(RunDBInterface):
2153
2493
 
2154
2494
  def list_entities(
2155
2495
  self,
2156
- project: str,
2157
- name: str = None,
2158
- tag: str = None,
2159
- labels: list[str] = None,
2496
+ project: Optional[str] = None,
2497
+ name: Optional[str] = None,
2498
+ tag: Optional[str] = None,
2499
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2160
2500
  ) -> list[dict]:
2161
2501
  """Retrieve a list of entities and their mapping to the containing feature-sets. This function is similar
2162
2502
  to the :py:func:`~list_features` function, and uses the same logic. However, the entities are matched
2163
2503
  against the name rather than the features.
2504
+
2505
+ :param project: The project containing the entities.
2506
+ :param name: The name of the entities to retrieve.
2507
+ :param tag: The tag of the specific entity version to retrieve.
2508
+ :param labels: Filter entities by label key-value pairs or key existence. This can be provided as:
2509
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2510
+ or `{"label": None}` to check for key existence.
2511
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2512
+ or just `"label"` for key existence.
2513
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2514
+ the specified key-value pairs or key existence.
2515
+ :returns: A list of entities.
2164
2516
  """
2165
2517
 
2166
2518
  project = project or config.default_project
2519
+ labels = self._parse_labels(labels)
2167
2520
  params = {
2168
2521
  "name": name,
2169
2522
  "tag": tag,
2170
- "label": labels or [],
2523
+ "label": labels,
2171
2524
  }
2172
2525
 
2173
2526
  path = f"projects/{project}/entities"
@@ -2178,21 +2531,34 @@ class HTTPRunDB(RunDBInterface):
2178
2531
 
2179
2532
  def list_entities_v2(
2180
2533
  self,
2181
- project: str,
2182
- name: str = None,
2183
- tag: str = None,
2184
- labels: list[str] = None,
2534
+ project: Optional[str] = None,
2535
+ name: Optional[str] = None,
2536
+ tag: Optional[str] = None,
2537
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2185
2538
  ) -> dict[str, list[dict]]:
2186
2539
  """Retrieve a list of entities and their mapping to the containing feature-sets. This function is similar
2187
2540
  to the :py:func:`~list_features_v2` function, and uses the same logic. However, the entities are matched
2188
2541
  against the name rather than the features.
2542
+
2543
+ :param project: The project containing the entities.
2544
+ :param name: The name of the entities to retrieve.
2545
+ :param tag: The tag of the specific entity version to retrieve.
2546
+ :param labels: Filter entities by label key-value pairs or key existence. This can be provided as:
2547
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2548
+ or `{"label": None}` to check for key existence.
2549
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2550
+ or just `"label"` for key existence.
2551
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2552
+ the specified key-value pairs or key existence.
2553
+ :returns: A list of entities.
2189
2554
  """
2190
2555
 
2191
2556
  project = project or config.default_project
2557
+ labels = self._parse_labels(labels)
2192
2558
  params = {
2193
2559
  "name": name,
2194
2560
  "tag": tag,
2195
- "label": labels or [],
2561
+ "label": labels,
2196
2562
  }
2197
2563
 
2198
2564
  path = f"projects/{project}/entities"
@@ -2203,7 +2569,6 @@ class HTTPRunDB(RunDBInterface):
2203
2569
 
2204
2570
  @staticmethod
2205
2571
  def _generate_partition_by_params(
2206
- partition_by_cls,
2207
2572
  partition_by,
2208
2573
  rows_per_partition,
2209
2574
  sort_by,
@@ -2222,13 +2587,13 @@ class HTTPRunDB(RunDBInterface):
2222
2587
 
2223
2588
  def list_feature_sets(
2224
2589
  self,
2225
- project: str = "",
2226
- name: str = None,
2227
- tag: str = None,
2228
- state: str = None,
2229
- entities: list[str] = None,
2230
- features: list[str] = None,
2231
- labels: list[str] = None,
2590
+ project: Optional[str] = None,
2591
+ name: Optional[str] = None,
2592
+ tag: Optional[str] = None,
2593
+ state: Optional[str] = None,
2594
+ entities: Optional[list[str]] = None,
2595
+ features: Optional[list[str]] = None,
2596
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2232
2597
  partition_by: Union[
2233
2598
  mlrun.common.schemas.FeatureStorePartitionByField, str
2234
2599
  ] = None,
@@ -2249,7 +2614,13 @@ class HTTPRunDB(RunDBInterface):
2249
2614
  :param state: Match feature-sets with a specific state.
2250
2615
  :param entities: Match feature-sets which contain entities whose name is in this list.
2251
2616
  :param features: Match feature-sets which contain features whose name is in this list.
2252
- :param labels: Match feature-sets which have these labels.
2617
+ :param labels: Filter feature-sets by label key-value pairs or key existence. This can be provided as:
2618
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2619
+ or `{"label": None}` to check for key existence.
2620
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2621
+ or just `"label"` for key existence.
2622
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2623
+ the specified key-value pairs or key existence.
2253
2624
  :param partition_by: Field to group results by. Only allowed value is `name`. When `partition_by` is specified,
2254
2625
  the `partition_sort_by` parameter must be provided as well.
2255
2626
  :param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
@@ -2264,20 +2635,19 @@ class HTTPRunDB(RunDBInterface):
2264
2635
  """
2265
2636
 
2266
2637
  project = project or config.default_project
2267
-
2638
+ labels = self._parse_labels(labels)
2268
2639
  params = {
2269
2640
  "name": name,
2270
2641
  "state": state,
2271
2642
  "tag": tag,
2272
2643
  "entity": entities or [],
2273
2644
  "feature": features or [],
2274
- "label": labels or [],
2645
+ "label": labels,
2275
2646
  "format": format_,
2276
2647
  }
2277
2648
  if partition_by:
2278
2649
  params.update(
2279
2650
  self._generate_partition_by_params(
2280
- mlrun.common.schemas.FeatureStorePartitionByField,
2281
2651
  partition_by,
2282
2652
  rows_per_partition,
2283
2653
  partition_sort_by,
@@ -2436,7 +2806,11 @@ class HTTPRunDB(RunDBInterface):
2436
2806
  return resp.json()
2437
2807
 
2438
2808
  def get_feature_vector(
2439
- self, name: str, project: str = "", tag: str = None, uid: str = None
2809
+ self,
2810
+ name: str,
2811
+ project: str = "",
2812
+ tag: Optional[str] = None,
2813
+ uid: Optional[str] = None,
2440
2814
  ) -> FeatureVector:
2441
2815
  """Return a specific feature-vector referenced by its tag or uid. If none are provided, ``latest`` tag will
2442
2816
  be used."""
@@ -2450,11 +2824,11 @@ class HTTPRunDB(RunDBInterface):
2450
2824
 
2451
2825
  def list_feature_vectors(
2452
2826
  self,
2453
- project: str = "",
2454
- name: str = None,
2455
- tag: str = None,
2456
- state: str = None,
2457
- labels: list[str] = None,
2827
+ project: Optional[str] = None,
2828
+ name: Optional[str] = None,
2829
+ tag: Optional[str] = None,
2830
+ state: Optional[str] = None,
2831
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2458
2832
  partition_by: Union[
2459
2833
  mlrun.common.schemas.FeatureStorePartitionByField, str
2460
2834
  ] = None,
@@ -2470,7 +2844,13 @@ class HTTPRunDB(RunDBInterface):
2470
2844
  :param name: Name of feature-vector to match. This is a like query, and is case-insensitive.
2471
2845
  :param tag: Match feature-vectors with specific tag.
2472
2846
  :param state: Match feature-vectors with a specific state.
2473
- :param labels: Match feature-vectors which have these labels.
2847
+ :param labels: Filter feature-vectors by label key-value pairs or key existence. This can be provided as:
2848
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2849
+ or `{"label": None}` to check for key existence.
2850
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2851
+ or just `"label"` for key existence.
2852
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2853
+ the specified key-value pairs or key existence.
2474
2854
  :param partition_by: Field to group results by. Only allowed value is `name`. When `partition_by` is specified,
2475
2855
  the `partition_sort_by` parameter must be provided as well.
2476
2856
  :param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
@@ -2482,17 +2862,16 @@ class HTTPRunDB(RunDBInterface):
2482
2862
  """
2483
2863
 
2484
2864
  project = project or config.default_project
2485
-
2865
+ labels = self._parse_labels(labels)
2486
2866
  params = {
2487
2867
  "name": name,
2488
2868
  "state": state,
2489
2869
  "tag": tag,
2490
- "label": labels or [],
2870
+ "label": labels,
2491
2871
  }
2492
2872
  if partition_by:
2493
2873
  params.update(
2494
2874
  self._generate_partition_by_params(
2495
- mlrun.common.schemas.FeatureStorePartitionByField,
2496
2875
  partition_by,
2497
2876
  rows_per_partition,
2498
2877
  partition_sort_by,
@@ -2699,11 +3078,11 @@ class HTTPRunDB(RunDBInterface):
2699
3078
 
2700
3079
  def list_projects(
2701
3080
  self,
2702
- owner: str = None,
3081
+ owner: Optional[str] = None,
2703
3082
  format_: Union[
2704
3083
  str, mlrun.common.formatters.ProjectFormat
2705
3084
  ] = mlrun.common.formatters.ProjectFormat.name_only,
2706
- labels: list[str] = None,
3085
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2707
3086
  state: Union[str, mlrun.common.schemas.ProjectState] = None,
2708
3087
  ) -> list[Union[mlrun.projects.MlrunProject, str]]:
2709
3088
  """Return a list of the existing projects, potentially filtered by specific criteria.
@@ -2715,15 +3094,22 @@ class HTTPRunDB(RunDBInterface):
2715
3094
  - ``minimal`` - Return minimal project objects (minimization happens in the BE).
2716
3095
  - ``full`` - Return full project objects.
2717
3096
 
2718
- :param labels: Filter by labels attached to the project.
3097
+ :param labels: Filter projects by label key-value pairs or key existence. This can be provided as:
3098
+ - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
3099
+ or `{"label": None}` to check for key existence.
3100
+ - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
3101
+ or just `"label"` for key existence.
3102
+ - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
3103
+ the specified key-value pairs or key existence.
2719
3104
  :param state: Filter by project's state. Can be either ``online`` or ``archived``.
2720
3105
  """
3106
+ labels = self._parse_labels(labels)
2721
3107
 
2722
3108
  params = {
2723
3109
  "owner": owner,
2724
3110
  "state": state,
2725
3111
  "format": format_,
2726
- "label": labels or [],
3112
+ "label": labels,
2727
3113
  }
2728
3114
 
2729
3115
  error_message = f"Failed listing projects, query: {params}"
@@ -2739,7 +3125,7 @@ class HTTPRunDB(RunDBInterface):
2739
3125
  for project_dict in response.json()["projects"]
2740
3126
  ]
2741
3127
 
2742
- def get_project(self, name: str) -> mlrun.projects.MlrunProject:
3128
+ def get_project(self, name: str) -> "mlrun.MlrunProject":
2743
3129
  """Get details for a specific project."""
2744
3130
 
2745
3131
  if not name:
@@ -2748,7 +3134,7 @@ class HTTPRunDB(RunDBInterface):
2748
3134
  path = f"projects/{name}"
2749
3135
  error_message = f"Failed retrieving project {name}"
2750
3136
  response = self.api_call("GET", path, error_message)
2751
- return mlrun.projects.MlrunProject.from_dict(response.json())
3137
+ return mlrun.MlrunProject.from_dict(response.json())
2752
3138
 
2753
3139
  def delete_project(
2754
3140
  self,
@@ -2919,7 +3305,7 @@ class HTTPRunDB(RunDBInterface):
2919
3305
  provider: Union[
2920
3306
  str, mlrun.common.schemas.SecretProviderName
2921
3307
  ] = mlrun.common.schemas.SecretProviderName.kubernetes,
2922
- secrets: dict = None,
3308
+ secrets: Optional[dict] = None,
2923
3309
  ):
2924
3310
  """Create project-context secrets using either ``vault`` or ``kubernetes`` provider.
2925
3311
  When using with Vault, this will create needed Vault structures for storing secrets in project-context, and
@@ -2963,11 +3349,11 @@ class HTTPRunDB(RunDBInterface):
2963
3349
  def list_project_secrets(
2964
3350
  self,
2965
3351
  project: str,
2966
- token: str = None,
3352
+ token: Optional[str] = None,
2967
3353
  provider: Union[
2968
3354
  str, mlrun.common.schemas.SecretProviderName
2969
3355
  ] = mlrun.common.schemas.SecretProviderName.kubernetes,
2970
- secrets: list[str] = None,
3356
+ secrets: Optional[list[str]] = None,
2971
3357
  ) -> mlrun.common.schemas.SecretsData:
2972
3358
  """Retrieve project-context secrets from Vault.
2973
3359
 
@@ -3010,7 +3396,7 @@ class HTTPRunDB(RunDBInterface):
3010
3396
  provider: Union[
3011
3397
  str, mlrun.common.schemas.SecretProviderName
3012
3398
  ] = mlrun.common.schemas.SecretProviderName.kubernetes,
3013
- token: str = None,
3399
+ token: Optional[str] = None,
3014
3400
  ) -> mlrun.common.schemas.SecretKeysData:
3015
3401
  """Retrieve project-context secret keys from Vault or Kubernetes.
3016
3402
 
@@ -3056,7 +3442,7 @@ class HTTPRunDB(RunDBInterface):
3056
3442
  provider: Union[
3057
3443
  str, mlrun.common.schemas.SecretProviderName
3058
3444
  ] = mlrun.common.schemas.SecretProviderName.kubernetes,
3059
- secrets: list[str] = None,
3445
+ secrets: Optional[list[str]] = None,
3060
3446
  ):
3061
3447
  """Delete project-context secrets from Kubernetes.
3062
3448
 
@@ -3076,13 +3462,44 @@ class HTTPRunDB(RunDBInterface):
3076
3462
  params=params,
3077
3463
  )
3078
3464
 
3465
+ def get_model_endpoint_monitoring_metrics(
3466
+ self,
3467
+ project: str,
3468
+ endpoint_id: str,
3469
+ type: Literal["results", "metrics", "all"] = "all",
3470
+ ) -> list[mm_endpoints.ModelEndpointMonitoringMetric]:
3471
+ """Get application metrics/results by endpoint id and project.
3472
+
3473
+ :param project: The name of the project.
3474
+ :param endpoint_id: The unique id of the model endpoint.
3475
+ :param type: The type of the metrics to return. "all" means "results" and "metrics".
3476
+
3477
+ :return: A list of the application metrics or/and results for this model endpoint.
3478
+ """
3479
+ path = f"projects/{project}/model-endpoints/{endpoint_id}/metrics"
3480
+ params = {"type": type}
3481
+ error_message = (
3482
+ f"Failed to get model endpoint monitoring metrics,"
3483
+ f" endpoint_id: {endpoint_id}, project: {project}"
3484
+ )
3485
+ response = self.api_call(
3486
+ mlrun.common.types.HTTPMethod.GET,
3487
+ path,
3488
+ error_message,
3489
+ params=params,
3490
+ )
3491
+ monitoring_metrics = response.json()
3492
+ return parse_obj_as(
3493
+ list[mm_endpoints.ModelEndpointMonitoringMetric], monitoring_metrics
3494
+ )
3495
+
3079
3496
  def create_user_secrets(
3080
3497
  self,
3081
3498
  user: str,
3082
3499
  provider: Union[
3083
3500
  str, mlrun.common.schemas.SecretProviderName
3084
3501
  ] = mlrun.common.schemas.SecretProviderName.vault,
3085
- secrets: dict = None,
3502
+ secrets: Optional[dict] = None,
3086
3503
  ):
3087
3504
  """Create user-context secret in Vault. Please refer to :py:func:`create_project_secrets` for more details
3088
3505
  and status of this functionality.
@@ -3164,212 +3581,205 @@ class HTTPRunDB(RunDBInterface):
3164
3581
 
3165
3582
  def create_model_endpoint(
3166
3583
  self,
3167
- project: str,
3168
- endpoint_id: str,
3169
- model_endpoint: Union[
3170
- mlrun.model_monitoring.model_endpoint.ModelEndpoint, dict
3171
- ],
3172
- ):
3584
+ model_endpoint: mlrun.common.schemas.ModelEndpoint,
3585
+ ) -> mlrun.common.schemas.ModelEndpoint:
3173
3586
  """
3174
3587
  Creates a DB record with the given model_endpoint record.
3175
3588
 
3176
- :param project: The name of the project.
3177
- :param endpoint_id: The id of the endpoint.
3178
3589
  :param model_endpoint: An object representing the model endpoint.
3179
- """
3180
3590
 
3181
- if isinstance(
3182
- model_endpoint, mlrun.model_monitoring.model_endpoint.ModelEndpoint
3183
- ):
3184
- model_endpoint = model_endpoint.to_dict()
3591
+ :return: The created model endpoint object.
3592
+ """
3185
3593
 
3186
- path = f"projects/{project}/model-endpoints/{endpoint_id}"
3187
- self.api_call(
3188
- method="POST",
3594
+ path = f"projects/{model_endpoint.metadata.project}/model-endpoints"
3595
+ response = self.api_call(
3596
+ method=mlrun.common.types.HTTPMethod.POST,
3189
3597
  path=path,
3190
- body=dict_to_json(model_endpoint),
3598
+ body=model_endpoint.json(),
3191
3599
  )
3600
+ return mlrun.common.schemas.ModelEndpoint(**response.json())
3192
3601
 
3193
3602
  def delete_model_endpoint(
3194
3603
  self,
3604
+ name: str,
3195
3605
  project: str,
3196
- endpoint_id: str,
3606
+ function_name: Optional[str] = None,
3607
+ function_tag: Optional[str] = None,
3608
+ endpoint_id: Optional[str] = None,
3197
3609
  ):
3198
3610
  """
3199
3611
  Deletes the DB record of a given model endpoint, project and endpoint_id are used for lookup
3200
3612
 
3613
+ :param name: The name of the model endpoint
3201
3614
  :param project: The name of the project
3615
+ :param function_name: The name of the function
3616
+ :param function_tag: The tag of the function
3202
3617
  :param endpoint_id: The id of the endpoint
3203
3618
  """
3204
-
3205
- path = f"projects/{project}/model-endpoints/{endpoint_id}"
3619
+ self._check_model_endpoint_representation(
3620
+ function_name, function_tag, endpoint_id
3621
+ )
3622
+ path = f"projects/{project}/model-endpoints/{name}"
3206
3623
  self.api_call(
3207
- method="DELETE",
3624
+ method=mlrun.common.types.HTTPMethod.DELETE,
3208
3625
  path=path,
3626
+ params={
3627
+ "function_name": function_name,
3628
+ "function_tag": function_tag,
3629
+ "endpoint_id": endpoint_id,
3630
+ },
3209
3631
  )
3210
3632
 
3211
3633
  def list_model_endpoints(
3212
3634
  self,
3213
3635
  project: str,
3214
- model: Optional[str] = None,
3215
- function: Optional[str] = None,
3216
- labels: list[str] = None,
3217
- start: str = "now-1h",
3218
- end: str = "now",
3219
- metrics: Optional[list[str]] = None,
3636
+ name: Optional[str] = None,
3637
+ function_name: Optional[str] = None,
3638
+ function_tag: Optional[str] = None,
3639
+ model_name: Optional[str] = None,
3640
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
3641
+ start: Optional[datetime] = None,
3642
+ end: Optional[datetime] = None,
3643
+ tsdb_metrics: bool = True,
3220
3644
  top_level: bool = False,
3221
3645
  uids: Optional[list[str]] = None,
3222
- ) -> list[mlrun.model_monitoring.model_endpoint.ModelEndpoint]:
3646
+ latest_only: bool = False,
3647
+ ) -> mlrun.common.schemas.ModelEndpointList:
3648
+ """
3649
+ List model endpoints with optional filtering by name, function name, model name, labels, and time range.
3650
+
3651
+ :param project: The name of the project
3652
+ :param name: The name of the model endpoint
3653
+ :param function_name: The name of the function
3654
+ :param function_tag: The tag of the function
3655
+ :param model_name: The name of the model
3656
+ :param labels: A list of labels to filter by. (see mlrun.common.schemas.LabelsModel)
3657
+ :param start: The start time to filter by.Corresponding to the `created` field.
3658
+ :param end: The end time to filter by. Corresponding to the `created` field.
3659
+ :param tsdb_metrics: Whether to include metrics from the time series DB.
3660
+ :param top_level: Whether to return only top level model endpoints.
3661
+ :param uids: A list of unique ids to filter by.
3662
+ :param latest_only: Whether to return only the latest model endpoint version.
3663
+ :return: A list of model endpoints.
3223
3664
  """
3224
- Returns a list of `ModelEndpoint` objects. Each `ModelEndpoint` object represents the current state of a
3225
- model endpoint. This functions supports filtering by the following parameters:
3226
- 1) model
3227
- 2) function
3228
- 3) labels
3229
- 4) top level
3230
- 5) uids
3231
- By default, when no filters are applied, all available endpoints for the given project will be listed.
3232
-
3233
- In addition, this functions provides a facade for listing endpoint related metrics. This facade is time-based
3234
- and depends on the 'start' and 'end' parameters. By default, when the metrics parameter is None, no metrics are
3235
- added to the output of this function.
3236
-
3237
- :param project: The name of the project
3238
- :param model: The name of the model to filter by
3239
- :param function: The name of the function to filter by
3240
- :param labels: A list of labels to filter by. Label filters work by either filtering a specific value of a
3241
- label (i.e. list("key=value")) or by looking for the existence of a given key (i.e. "key")
3242
- :param metrics: A list of metrics to return for each endpoint, read more in 'TimeMetric'
3243
- :param start: The start time of the metrics. Can be represented by a string containing an RFC 3339 time, a
3244
- Unix timestamp in milliseconds, a relative time (`'now'` or `'now-[0-9]+[mhd]'`, where
3245
- `m` = minutes, `h` = hours, `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
3246
- :param end: The end time of the metrics. Can be represented by a string containing an RFC 3339 time, a
3247
- Unix timestamp in milliseconds, a relative time (`'now'` or `'now-[0-9]+[mhd]'`, where
3248
- `m` = minutes, `h` = hours, `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
3249
- :param top_level: if true will return only routers and endpoint that are NOT children of any router
3250
- :param uids: if passed will return a list `ModelEndpoint` object with uid in uids
3251
- """
3252
-
3253
3665
  path = f"projects/{project}/model-endpoints"
3254
-
3255
- if labels and isinstance(labels, dict):
3256
- labels = [f"{key}={value}" for key, value in labels.items()]
3666
+ labels = self._parse_labels(labels)
3257
3667
 
3258
3668
  response = self.api_call(
3259
- method="GET",
3669
+ method=mlrun.common.types.HTTPMethod.GET,
3260
3670
  path=path,
3261
3671
  params={
3262
- "model": model,
3263
- "function": function,
3264
- "label": labels or [],
3265
- "start": start,
3266
- "end": end,
3267
- "metric": metrics or [],
3672
+ "name": name,
3673
+ "model_name": model_name,
3674
+ "function_name": function_name,
3675
+ "function_tag": function_tag,
3676
+ "label": labels,
3677
+ "start": datetime_to_iso(start),
3678
+ "end": datetime_to_iso(end),
3679
+ "tsdb_metrics": tsdb_metrics,
3268
3680
  "top-level": top_level,
3269
3681
  "uid": uids,
3682
+ "latest_only": latest_only,
3270
3683
  },
3271
3684
  )
3272
3685
 
3273
- # Generate a list of a model endpoint dictionaries
3274
- model_endpoints = response.json()["endpoints"]
3275
- if model_endpoints:
3276
- return [
3277
- mlrun.model_monitoring.model_endpoint.ModelEndpoint.from_dict(obj)
3278
- for obj in model_endpoints
3279
- ]
3280
- return []
3686
+ return mlrun.common.schemas.ModelEndpointList(**response.json())
3281
3687
 
3282
3688
  def get_model_endpoint(
3283
3689
  self,
3690
+ name: str,
3284
3691
  project: str,
3285
- endpoint_id: str,
3286
- start: Optional[str] = None,
3287
- end: Optional[str] = None,
3288
- metrics: Optional[list[str]] = None,
3692
+ function_name: Optional[str] = None,
3693
+ function_tag: Optional[str] = None,
3694
+ endpoint_id: Optional[str] = None,
3695
+ tsdb_metrics: bool = True,
3289
3696
  feature_analysis: bool = False,
3290
- ) -> mlrun.model_monitoring.model_endpoint.ModelEndpoint:
3697
+ ) -> mlrun.common.schemas.ModelEndpoint:
3291
3698
  """
3292
3699
  Returns a single `ModelEndpoint` object with additional metrics and feature related data.
3293
3700
 
3701
+ :param name: The name of the model endpoint
3294
3702
  :param project: The name of the project
3295
- :param endpoint_id: The unique id of the model endpoint.
3296
- :param start: The start time of the metrics. Can be represented by a string containing an
3297
- RFC 3339 time, a Unix timestamp in milliseconds, a relative time
3298
- (`'now'` or `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours,
3299
- `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
3300
- :param end: The end time of the metrics. Can be represented by a string containing an
3301
- RFC 3339 time, a Unix timestamp in milliseconds, a relative time
3302
- (`'now'` or `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours,
3303
- `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
3304
- :param metrics: A list of metrics to return for the model endpoint. There are pre-defined
3305
- metrics for model endpoints such as predictions_per_second and
3306
- latency_avg_5m but also custom metrics defined by the user. Please note that
3307
- these metrics are stored in the time series DB and the results will be
3308
- appeared under model_endpoint.spec.metrics.
3309
- :param feature_analysis: When True, the base feature statistics and current feature statistics will
3310
- be added to the output of the resulting object.
3311
-
3312
- :returns: A `ModelEndpoint` object.
3313
- """
3314
-
3315
- path = f"projects/{project}/model-endpoints/{endpoint_id}"
3703
+ :param function_name: The name of the function
3704
+ :param function_tag: The tag of the function
3705
+ :param endpoint_id: The id of the endpoint
3706
+ :param tsdb_metrics: Whether to include metrics from the time series DB.
3707
+ :param feature_analysis: Whether to include feature analysis data (feature_stats,
3708
+ current_stats & drift_measures).
3709
+
3710
+ :return: A `ModelEndpoint` object.
3711
+ """
3712
+ self._check_model_endpoint_representation(
3713
+ function_name, function_tag, endpoint_id
3714
+ )
3715
+ path = f"projects/{project}/model-endpoints/{name}"
3316
3716
  response = self.api_call(
3317
- method="GET",
3717
+ method=mlrun.common.types.HTTPMethod.GET,
3318
3718
  path=path,
3319
3719
  params={
3320
- "start": start,
3321
- "end": end,
3322
- "metric": metrics or [],
3720
+ "function_name": function_name,
3721
+ "function_tag": function_tag,
3722
+ "endpoint_id": endpoint_id,
3723
+ "tsdb_metrics": tsdb_metrics,
3323
3724
  "feature_analysis": feature_analysis,
3324
3725
  },
3325
3726
  )
3326
3727
 
3327
- return mlrun.model_monitoring.model_endpoint.ModelEndpoint.from_dict(
3328
- response.json()
3329
- )
3728
+ return mlrun.common.schemas.ModelEndpoint(**response.json())
3330
3729
 
3331
3730
  def patch_model_endpoint(
3332
3731
  self,
3732
+ name: str,
3333
3733
  project: str,
3334
- endpoint_id: str,
3335
3734
  attributes: dict,
3336
- ):
3337
- """
3338
- Updates model endpoint with provided attributes.
3339
-
3340
- :param project: The name of the project.
3341
- :param endpoint_id: The id of the endpoint.
3342
- :param attributes: Dictionary of attributes that will be used for update the model endpoint. The keys
3343
- of this dictionary should exist in the target table. Note that the values should be from type string or from
3344
- a valid numerical type such as int or float. More details about the model endpoint available attributes can
3345
- be found under :py:class:`~mlrun.common.schemas.ModelEndpoint`.
3346
-
3347
- Example::
3348
-
3349
- # Generate current stats for two features
3350
- current_stats = {'tvd_sum': 2.2,
3351
- 'tvd_mean': 0.5,
3352
- 'hellinger_sum': 3.6,
3353
- 'hellinger_mean': 0.9,
3354
- 'kld_sum': 24.2,
3355
- 'kld_mean': 6.0,
3356
- 'f1': {'tvd': 0.5, 'hellinger': 1.0, 'kld': 6.4},
3357
- 'f2': {'tvd': 0.5, 'hellinger': 1.0, 'kld': 6.5}}
3358
-
3359
- # Create attributes dictionary according to the required format
3360
- attributes = {`current_stats`: json.dumps(current_stats),
3361
- `drift_status`: "DRIFT_DETECTED"}
3362
-
3735
+ function_name: Optional[str] = None,
3736
+ function_tag: Optional[str] = None,
3737
+ endpoint_id: Optional[str] = None,
3738
+ ) -> mlrun.common.schemas.ModelEndpoint:
3363
3739
  """
3740
+ Updates a model endpoint with the given attributes.
3364
3741
 
3365
- attributes = {"attributes": _as_json(attributes)}
3366
- path = f"projects/{project}/model-endpoints/{endpoint_id}"
3367
- self.api_call(
3368
- method="PATCH",
3742
+ :param name: The name of the model endpoint
3743
+ :param project: The name of the project
3744
+ :param attributes: The attributes to update
3745
+ :param function_name: The name of the function
3746
+ :param function_tag: The tag of the function
3747
+ :param endpoint_id: The id of the endpoint
3748
+ :return: The updated `ModelEndpoint` object.
3749
+ """
3750
+ attributes_keys = list(attributes.keys())
3751
+ attributes["name"] = name
3752
+ attributes["project"] = project
3753
+ attributes["function_name"] = function_name or None
3754
+ attributes["function_tag"] = function_tag or None
3755
+ attributes["uid"] = endpoint_id or None
3756
+ model_endpoint = mlrun.common.schemas.ModelEndpoint.from_flat_dict(attributes)
3757
+ path = f"projects/{project}/model-endpoints"
3758
+ logger.info(
3759
+ "Patching model endpoint",
3760
+ attributes_keys=attributes_keys,
3761
+ model_endpoint=model_endpoint,
3762
+ )
3763
+ response = self.api_call(
3764
+ method=mlrun.common.types.HTTPMethod.PATCH,
3369
3765
  path=path,
3370
- params=attributes,
3766
+ params={
3767
+ "attribute-key": attributes_keys,
3768
+ },
3769
+ body=model_endpoint.json(),
3371
3770
  )
3372
3771
 
3772
+ return mlrun.common.schemas.ModelEndpoint(**response.json())
3773
+
3774
+ @staticmethod
3775
+ def _check_model_endpoint_representation(
3776
+ function_name: str, function_tag: str, uid: str
3777
+ ):
3778
+ if not uid and not (function_name and function_tag):
3779
+ raise MLRunInvalidArgumentError(
3780
+ "Either endpoint_uid or function_name and function_tag must be provided"
3781
+ )
3782
+
3373
3783
  def update_model_monitoring_controller(
3374
3784
  self,
3375
3785
  project: str,
@@ -3441,7 +3851,7 @@ class HTTPRunDB(RunDBInterface):
3441
3851
  delete_stream_function: bool = False,
3442
3852
  delete_histogram_data_drift_app: bool = True,
3443
3853
  delete_user_applications: bool = False,
3444
- user_application_list: list[str] = None,
3854
+ user_application_list: Optional[list[str]] = None,
3445
3855
  ) -> bool:
3446
3856
  """
3447
3857
  Disable model monitoring application controller, writer, stream, histogram data drift application
@@ -3714,8 +4124,8 @@ class HTTPRunDB(RunDBInterface):
3714
4124
  def get_hub_catalog(
3715
4125
  self,
3716
4126
  source_name: str,
3717
- version: str = None,
3718
- tag: str = None,
4127
+ version: Optional[str] = None,
4128
+ tag: Optional[str] = None,
3719
4129
  force_refresh: bool = False,
3720
4130
  ):
3721
4131
  """
@@ -3745,7 +4155,7 @@ class HTTPRunDB(RunDBInterface):
3745
4155
  self,
3746
4156
  source_name: str,
3747
4157
  item_name: str,
3748
- version: str = None,
4158
+ version: Optional[str] = None,
3749
4159
  tag: str = "latest",
3750
4160
  force_refresh: bool = False,
3751
4161
  ):
@@ -3775,7 +4185,7 @@ class HTTPRunDB(RunDBInterface):
3775
4185
  source_name: str,
3776
4186
  item_name: str,
3777
4187
  asset_name: str,
3778
- version: str = None,
4188
+ version: Optional[str] = None,
3779
4189
  tag: str = "latest",
3780
4190
  ):
3781
4191
  """
@@ -3897,18 +4307,27 @@ class HTTPRunDB(RunDBInterface):
3897
4307
  "operations/migrations",
3898
4308
  "Failed triggering migrations",
3899
4309
  )
3900
- if response.status_code == http.HTTPStatus.ACCEPTED:
3901
- background_task = mlrun.common.schemas.BackgroundTask(**response.json())
3902
- return self._wait_for_background_task_to_reach_terminal_state(
3903
- background_task.metadata.name
3904
- )
3905
- return None
4310
+ return self._wait_for_background_task_from_response(response)
4311
+
4312
+ def refresh_smtp_configuration(
4313
+ self,
4314
+ ) -> Optional[mlrun.common.schemas.BackgroundTask]:
4315
+ """Refresh smtp configuration and wait for the task to finish
4316
+
4317
+ :returns: :py:class:`~mlrun.common.schemas.BackgroundTask`.
4318
+ """
4319
+ response = self.api_call(
4320
+ "POST",
4321
+ "operations/refresh-smtp-configuration",
4322
+ "Failed refreshing smtp configuration",
4323
+ )
4324
+ return self._wait_for_background_task_from_response(response)
3906
4325
 
3907
4326
  def set_run_notifications(
3908
4327
  self,
3909
4328
  project: str,
3910
4329
  run_uid: str,
3911
- notifications: list[mlrun.model.Notification] = None,
4330
+ notifications: Optional[list[mlrun.model.Notification]] = None,
3912
4331
  ):
3913
4332
  """
3914
4333
  Set notifications on a run. This will override any existing notifications on the run.
@@ -3934,7 +4353,7 @@ class HTTPRunDB(RunDBInterface):
3934
4353
  self,
3935
4354
  project: str,
3936
4355
  schedule_name: str,
3937
- notifications: list[mlrun.model.Notification] = None,
4356
+ notifications: Optional[list[mlrun.model.Notification]] = None,
3938
4357
  ):
3939
4358
  """
3940
4359
  Set notifications on a schedule. This will override any existing notifications on the schedule.
@@ -3960,7 +4379,7 @@ class HTTPRunDB(RunDBInterface):
3960
4379
  self,
3961
4380
  notification_objects: list[mlrun.model.Notification],
3962
4381
  run_uid: str,
3963
- project: str = None,
4382
+ project: Optional[str] = None,
3964
4383
  mask_params: bool = True,
3965
4384
  ):
3966
4385
  """
@@ -3994,7 +4413,7 @@ class HTTPRunDB(RunDBInterface):
3994
4413
  source: Optional[str] = None,
3995
4414
  run_name: Optional[str] = None,
3996
4415
  namespace: Optional[str] = None,
3997
- notifications: list[mlrun.model.Notification] = None,
4416
+ notifications: Optional[list[mlrun.model.Notification]] = None,
3998
4417
  ) -> mlrun.common.schemas.WorkflowResponse:
3999
4418
  """
4000
4419
  Submitting workflow for a remote execution.
@@ -4216,6 +4635,7 @@ class HTTPRunDB(RunDBInterface):
4216
4635
  alert_name: str,
4217
4636
  alert_data: Union[dict, AlertConfig],
4218
4637
  project="",
4638
+ force_reset: bool = False,
4219
4639
  ) -> AlertConfig:
4220
4640
  """
4221
4641
  Create/modify an alert.
@@ -4223,6 +4643,7 @@ class HTTPRunDB(RunDBInterface):
4223
4643
  :param alert_name: The name of the alert.
4224
4644
  :param alert_data: The data of the alert.
4225
4645
  :param project: The project that the alert belongs to.
4646
+ :param force_reset: If True and the alert already exists, the alert would be reset.
4226
4647
  :returns: The created/modified alert.
4227
4648
  """
4228
4649
  if not alert_data:
@@ -4247,7 +4668,10 @@ class HTTPRunDB(RunDBInterface):
4247
4668
 
4248
4669
  alert_data = alert_instance.to_dict()
4249
4670
  body = _as_json(alert_data)
4250
- response = self.api_call("PUT", endpoint_path, error_message, body=body)
4671
+ params = {"force_reset": bool2str(force_reset)} if force_reset else {}
4672
+ response = self.api_call(
4673
+ "PUT", endpoint_path, error_message, params=params, body=body
4674
+ )
4251
4675
  return AlertConfig.from_dict(response.json())
4252
4676
 
4253
4677
  def get_alert_config(self, alert_name: str, project="") -> AlertConfig:
@@ -4334,6 +4758,460 @@ class HTTPRunDB(RunDBInterface):
4334
4758
  results.append(mlrun.common.schemas.AlertTemplate(**item))
4335
4759
  return results
4336
4760
 
4761
+ def list_alert_activations(
4762
+ self,
4763
+ project: Optional[str] = None,
4764
+ name: Optional[str] = None,
4765
+ since: Optional[datetime] = None,
4766
+ until: Optional[datetime] = None,
4767
+ entity: Optional[str] = None,
4768
+ severity: Optional[
4769
+ list[Union[mlrun.common.schemas.alert.AlertSeverity, str]]
4770
+ ] = None,
4771
+ entity_kind: Optional[
4772
+ Union[mlrun.common.schemas.alert.EventEntityKind, str]
4773
+ ] = None,
4774
+ event_kind: Optional[Union[mlrun.common.schemas.alert.EventKind, str]] = None,
4775
+ ) -> mlrun.common.schemas.AlertActivations:
4776
+ """
4777
+ Retrieve a list of all alert activations.
4778
+
4779
+ :param project: The project name to filter by. If None, results are not filtered by project.
4780
+ :param name: The alert name to filter by. Supports exact matching or partial matching if prefixed with `~`.
4781
+ :param since: Filters for alert activations occurring after this timestamp.
4782
+ :param until: Filters for alert activations occurring before this timestamp.
4783
+ :param entity: The entity ID to filter by. Supports wildcard matching if prefixed with `~`.
4784
+ :param severity: A list of severity levels to filter by (e.g., ["high", "low"]).
4785
+ :param entity_kind: The kind of entity (e.g., "job", "endpoint") to filter by.
4786
+ :param event_kind: The kind of event (e.g., ""data-drift-detected"", "failed") to filter by.
4787
+
4788
+ :returns: A list of alert activations matching the provided filters.
4789
+ """
4790
+
4791
+ alert_activations, _ = self._list_alert_activations(
4792
+ project=project,
4793
+ name=name,
4794
+ since=since,
4795
+ until=until,
4796
+ entity=entity,
4797
+ severity=severity,
4798
+ entity_kind=entity_kind,
4799
+ event_kind=event_kind,
4800
+ return_all=True,
4801
+ )
4802
+ return alert_activations
4803
+
4804
+ def paginated_list_alert_activations(
4805
+ self,
4806
+ *args,
4807
+ page: Optional[int] = None,
4808
+ page_size: Optional[int] = None,
4809
+ page_token: Optional[str] = None,
4810
+ **kwargs,
4811
+ ) -> tuple[AlertActivations, Optional[str]]:
4812
+ """List alerts activations with support for pagination and various filtering options.
4813
+
4814
+ This method retrieves a paginated list of alert activations based on the specified filter parameters.
4815
+ Pagination is controlled using the `page`, `page_size`, and `page_token` parameters. The method
4816
+ will return a list of alert activations that match the filtering criteria provided.
4817
+
4818
+ For detailed information about the parameters, refer to the list_alert_activations method:
4819
+ See :py:func:`~list_alert_activations` for more details.
4820
+
4821
+ Examples::
4822
+
4823
+ # Fetch first page of alert activations with page size of 5
4824
+ alert_activations, token = db.paginated_list_alert_activations(
4825
+ project="my-project", page_size=5
4826
+ )
4827
+ # Fetch next page using the pagination token from the previous response
4828
+ alert_activations, token = db.paginated_list_alert_activations(
4829
+ project="my-project", page_token=token
4830
+ )
4831
+ # Fetch alert activations for a specific page (e.g., page 3)
4832
+ alert_activations, token = db.paginated_list_alert_activations(
4833
+ project="my-project", page=3, page_size=5
4834
+ )
4835
+
4836
+ # Automatically iterate over all pages without explicitly specifying the page number
4837
+ alert_activations = []
4838
+ token = None
4839
+ while True:
4840
+ page_alert_activations, token = db.paginated_list_alert_activations(
4841
+ project="my-project", page_token=token, page_size=5
4842
+ )
4843
+ alert_activations.extend(page_alert_activations)
4844
+
4845
+ # If token is None and page_alert_activations is empty, we've reached the end (no more activations).
4846
+ # If token is None and page_alert_activations is not empty, we've fetched the last page of activations.
4847
+ if not token:
4848
+ break
4849
+ print(f"Total alert activations retrieved: {len(alert_activations)}")
4850
+
4851
+ :param page: The page number to retrieve. If not provided, the next page will be retrieved.
4852
+ :param page_size: The number of items per page to retrieve. Up to `page_size` responses are expected.
4853
+ :param page_token: A pagination token used to retrieve the next page of results. Should not be provided
4854
+ for the first request.
4855
+
4856
+ :returns: A tuple containing the list of alert activations and an optional `page_token` for pagination.
4857
+ """
4858
+ return self._list_alert_activations(
4859
+ *args,
4860
+ page=page,
4861
+ page_size=page_size,
4862
+ page_token=page_token,
4863
+ return_all=False,
4864
+ **kwargs,
4865
+ )
4866
+
4867
+ def get_project_summary(
4868
+ self, project: Optional[str] = None
4869
+ ) -> mlrun.common.schemas.ProjectSummary:
4870
+ """
4871
+ Retrieve the summary of a project.
4872
+
4873
+ :param project: Project name for which the summary belongs.
4874
+ :returns: A summary of the project.
4875
+ """
4876
+ project = project or config.default_project
4877
+
4878
+ endpoint_path = f"project-summaries/{project}"
4879
+ error_message = f"Failed retrieving project summary for {project}"
4880
+ response = self.api_call("GET", endpoint_path, error_message)
4881
+ return mlrun.common.schemas.ProjectSummary(**response.json())
4882
+
4883
+ @staticmethod
4884
+ def _parse_labels(
4885
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]],
4886
+ ):
4887
+ """
4888
+ Parse labels to support providing a dictionary from the SDK,
4889
+ which may not be directly supported in the endpoints.
4890
+
4891
+ :param labels: The labels to parse, which can be a dictionary, a list of strings,
4892
+ or a comma-separated string. This function converts them into a list
4893
+ of labels in the format 'key=value' or 'key'.
4894
+ :return: A list of parsed labels in the format 'key=value' or 'key'.
4895
+ :raises MLRunValueError: If the labels format is invalid.
4896
+ """
4897
+ try:
4898
+ return mlrun.common.schemas.common.LabelsModel(labels=labels).labels
4899
+ except pydantic.v1.error_wrappers.ValidationError as exc:
4900
+ raise mlrun.errors.MLRunValueError(
4901
+ "Invalid labels format. Must be a dictionary of strings, a list of strings, "
4902
+ "or a comma-separated string."
4903
+ ) from exc
4904
+
4905
+ def _list_artifacts(
4906
+ self,
4907
+ name: Optional[str] = None,
4908
+ project: Optional[str] = None,
4909
+ tag: Optional[str] = None,
4910
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
4911
+ since: Optional[datetime] = None,
4912
+ until: Optional[datetime] = None,
4913
+ iter: Optional[int] = None,
4914
+ best_iteration: bool = False,
4915
+ kind: Optional[str] = None,
4916
+ category: Union[str, mlrun.common.schemas.ArtifactCategories] = None,
4917
+ tree: Optional[str] = None,
4918
+ producer_uri: Optional[str] = None,
4919
+ format_: Optional[
4920
+ mlrun.common.formatters.ArtifactFormat
4921
+ ] = mlrun.common.formatters.ArtifactFormat.full,
4922
+ limit: Optional[int] = None,
4923
+ partition_by: Optional[
4924
+ Union[mlrun.common.schemas.ArtifactPartitionByField, str]
4925
+ ] = None,
4926
+ rows_per_partition: int = 1,
4927
+ partition_sort_by: Optional[
4928
+ Union[mlrun.common.schemas.SortField, str]
4929
+ ] = mlrun.common.schemas.SortField.updated,
4930
+ partition_order: Union[
4931
+ mlrun.common.schemas.OrderType, str
4932
+ ] = mlrun.common.schemas.OrderType.desc,
4933
+ page: Optional[int] = None,
4934
+ page_size: Optional[int] = None,
4935
+ page_token: Optional[str] = None,
4936
+ return_all: bool = False,
4937
+ ) -> tuple[ArtifactList, Optional[str]]:
4938
+ """Handles list artifacts, both paginated and not."""
4939
+
4940
+ project = project or config.default_project
4941
+ labels = self._parse_labels(labels)
4942
+
4943
+ params = {
4944
+ "name": name,
4945
+ "tag": tag,
4946
+ "label": labels,
4947
+ "iter": iter,
4948
+ "best-iteration": best_iteration,
4949
+ "kind": kind,
4950
+ "category": category,
4951
+ "tree": tree,
4952
+ "format": format_,
4953
+ "producer_uri": producer_uri,
4954
+ "since": datetime_to_iso(since),
4955
+ "until": datetime_to_iso(until),
4956
+ "limit": limit,
4957
+ "page": page,
4958
+ "page-size": page_size,
4959
+ "page-token": page_token,
4960
+ }
4961
+
4962
+ if partition_by:
4963
+ params.update(
4964
+ self._generate_partition_by_params(
4965
+ partition_by,
4966
+ rows_per_partition,
4967
+ partition_sort_by,
4968
+ partition_order,
4969
+ )
4970
+ )
4971
+ error = "list artifacts"
4972
+ endpoint_path = f"projects/{project}/artifacts"
4973
+
4974
+ # Fetch the responses, either one page or all based on `return_all`
4975
+ responses = self.paginated_api_call(
4976
+ "GET",
4977
+ endpoint_path,
4978
+ error,
4979
+ params=params,
4980
+ version="v2",
4981
+ return_all=return_all,
4982
+ )
4983
+ paginated_responses, token = self.process_paginated_responses(
4984
+ responses, "artifacts"
4985
+ )
4986
+
4987
+ values = ArtifactList(paginated_responses)
4988
+ values.tag = tag
4989
+ return values, token
4990
+
4991
+ def _list_functions(
4992
+ self,
4993
+ name: Optional[str] = None,
4994
+ project: Optional[str] = None,
4995
+ tag: Optional[str] = None,
4996
+ kind: Optional[str] = None,
4997
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
4998
+ format_: Optional[str] = None,
4999
+ since: Optional[datetime] = None,
5000
+ until: Optional[datetime] = None,
5001
+ page: Optional[int] = None,
5002
+ page_size: Optional[int] = None,
5003
+ page_token: Optional[str] = None,
5004
+ return_all: bool = False,
5005
+ ) -> tuple[list, Optional[str]]:
5006
+ """Handles list functions, both paginated and not."""
5007
+
5008
+ project = project or config.default_project
5009
+ labels = self._parse_labels(labels)
5010
+ params = {
5011
+ "name": name,
5012
+ "tag": tag,
5013
+ "kind": kind,
5014
+ "label": labels,
5015
+ "since": datetime_to_iso(since),
5016
+ "until": datetime_to_iso(until),
5017
+ "format": format_,
5018
+ "page": page,
5019
+ "page-size": page_size,
5020
+ "page-token": page_token,
5021
+ }
5022
+ error = "list functions"
5023
+ path = f"projects/{project}/functions"
5024
+
5025
+ # Fetch the responses, either one page or all based on `return_all`
5026
+ responses = self.paginated_api_call(
5027
+ "GET", path, error, params=params, return_all=return_all
5028
+ )
5029
+ paginated_responses, token = self.process_paginated_responses(
5030
+ responses, "funcs"
5031
+ )
5032
+ return paginated_responses, token
5033
+
5034
+ def _list_runs(
5035
+ self,
5036
+ name: Optional[str] = None,
5037
+ uid: Optional[Union[str, list[str]]] = None,
5038
+ project: Optional[str] = None,
5039
+ labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
5040
+ state: Optional[
5041
+ mlrun.common.runtimes.constants.RunStates
5042
+ ] = None, # Backward compatibility
5043
+ states: typing.Optional[list[mlrun.common.runtimes.constants.RunStates]] = None,
5044
+ sort: bool = True,
5045
+ last: int = 0,
5046
+ iter: bool = False,
5047
+ start_time_from: Optional[datetime] = None,
5048
+ start_time_to: Optional[datetime] = None,
5049
+ last_update_time_from: Optional[datetime] = None,
5050
+ last_update_time_to: Optional[datetime] = None,
5051
+ partition_by: Optional[
5052
+ Union[mlrun.common.schemas.RunPartitionByField, str]
5053
+ ] = None,
5054
+ rows_per_partition: int = 1,
5055
+ partition_sort_by: Optional[Union[mlrun.common.schemas.SortField, str]] = None,
5056
+ partition_order: Union[
5057
+ mlrun.common.schemas.OrderType, str
5058
+ ] = mlrun.common.schemas.OrderType.desc,
5059
+ max_partitions: int = 0,
5060
+ with_notifications: bool = False,
5061
+ page: Optional[int] = None,
5062
+ page_size: Optional[int] = None,
5063
+ page_token: Optional[str] = None,
5064
+ return_all: bool = False,
5065
+ ) -> tuple[RunList, Optional[str]]:
5066
+ """Handles list runs, both paginated and not."""
5067
+
5068
+ project = project or config.default_project
5069
+ if with_notifications:
5070
+ logger.warning(
5071
+ "Local run notifications are not persisted in the DB, therefore local runs will not be returned when "
5072
+ "using the `with_notifications` flag."
5073
+ )
5074
+
5075
+ if last:
5076
+ # TODO: Remove this in 1.8.0
5077
+ warnings.warn(
5078
+ "'last' is deprecated and will be removed in 1.8.0.",
5079
+ FutureWarning,
5080
+ )
5081
+
5082
+ if state:
5083
+ # TODO: Remove this in 1.9.0
5084
+ warnings.warn(
5085
+ "'state' is deprecated and will be removed in 1.9.0. Use 'states' instead.",
5086
+ FutureWarning,
5087
+ )
5088
+
5089
+ labels = self._parse_labels(labels)
5090
+
5091
+ if (
5092
+ not name
5093
+ and not uid
5094
+ and not labels
5095
+ and not state
5096
+ and not states
5097
+ and not last
5098
+ and not start_time_from
5099
+ and not start_time_to
5100
+ and not last_update_time_from
5101
+ and not last_update_time_to
5102
+ and not partition_by
5103
+ and not partition_sort_by
5104
+ and not iter
5105
+ ):
5106
+ # default to last week on no filter
5107
+ start_time_from = datetime.now() - timedelta(days=7)
5108
+ partition_by = mlrun.common.schemas.RunPartitionByField.project_and_name
5109
+ partition_sort_by = mlrun.common.schemas.SortField.updated
5110
+
5111
+ params = {
5112
+ "name": name,
5113
+ "uid": uid,
5114
+ "label": labels,
5115
+ "state": (
5116
+ mlrun.utils.helpers.as_list(state)
5117
+ if state is not None
5118
+ else states or None
5119
+ ),
5120
+ "sort": bool2str(sort),
5121
+ "iter": bool2str(iter),
5122
+ "start_time_from": datetime_to_iso(start_time_from),
5123
+ "start_time_to": datetime_to_iso(start_time_to),
5124
+ "last_update_time_from": datetime_to_iso(last_update_time_from),
5125
+ "last_update_time_to": datetime_to_iso(last_update_time_to),
5126
+ "with-notifications": with_notifications,
5127
+ "page": page,
5128
+ "page-size": page_size,
5129
+ "page-token": page_token,
5130
+ }
5131
+
5132
+ if partition_by:
5133
+ params.update(
5134
+ self._generate_partition_by_params(
5135
+ partition_by,
5136
+ rows_per_partition,
5137
+ partition_sort_by,
5138
+ partition_order,
5139
+ max_partitions,
5140
+ )
5141
+ )
5142
+ error = "list runs"
5143
+ _path = self._path_of("runs", project)
5144
+
5145
+ # Fetch the responses, either one page or all based on `return_all`
5146
+ responses = self.paginated_api_call(
5147
+ "GET", _path, error, params=params, return_all=return_all
5148
+ )
5149
+ paginated_responses, token = self.process_paginated_responses(responses, "runs")
5150
+ return RunList(paginated_responses), token
5151
+
5152
+ def _list_alert_activations(
5153
+ self,
5154
+ project: Optional[str] = None,
5155
+ name: Optional[str] = None,
5156
+ since: Optional[datetime] = None,
5157
+ until: Optional[datetime] = None,
5158
+ entity: Optional[str] = None,
5159
+ severity: Optional[
5160
+ Union[
5161
+ mlrun.common.schemas.alert.AlertSeverity,
5162
+ str,
5163
+ list[Union[mlrun.common.schemas.alert.AlertSeverity, str]],
5164
+ ]
5165
+ ] = None,
5166
+ entity_kind: Optional[
5167
+ Union[mlrun.common.schemas.alert.EventEntityKind, str]
5168
+ ] = None,
5169
+ event_kind: Optional[Union[mlrun.common.schemas.alert.EventKind, str]] = None,
5170
+ page: Optional[int] = None,
5171
+ page_size: Optional[int] = None,
5172
+ page_token: Optional[str] = None,
5173
+ return_all: bool = False,
5174
+ ) -> tuple[mlrun.common.schemas.AlertActivations, Optional[str]]:
5175
+ project = project or config.default_project
5176
+ params = {
5177
+ "name": name,
5178
+ "since": datetime_to_iso(since),
5179
+ "until": datetime_to_iso(until),
5180
+ "entity": entity,
5181
+ "severity": mlrun.utils.helpers.as_list(severity) if severity else None,
5182
+ "entity-kind": entity_kind,
5183
+ "event-kind": event_kind,
5184
+ "page": page,
5185
+ "page-size": page_size,
5186
+ "page-token": page_token,
5187
+ }
5188
+ error = "list alert activations"
5189
+ path = f"projects/{project}/alert-activations"
5190
+
5191
+ # Fetch the responses, either one page or all based on `return_all`
5192
+ responses = self.paginated_api_call(
5193
+ "GET", path, error, params=params, return_all=return_all
5194
+ )
5195
+ paginated_responses, token = self.process_paginated_responses(
5196
+ responses, "activations"
5197
+ )
5198
+ paginated_results = mlrun.common.schemas.AlertActivations(
5199
+ activations=[
5200
+ mlrun.common.schemas.AlertActivation(**item)
5201
+ for item in paginated_responses
5202
+ ]
5203
+ )
5204
+
5205
+ return paginated_results, token
5206
+
5207
+ def _wait_for_background_task_from_response(self, response):
5208
+ if response.status_code == http.HTTPStatus.ACCEPTED:
5209
+ background_task = mlrun.common.schemas.BackgroundTask(**response.json())
5210
+ return self._wait_for_background_task_to_reach_terminal_state(
5211
+ background_task.metadata.name
5212
+ )
5213
+ return None
5214
+
4337
5215
 
4338
5216
  def _as_json(obj):
4339
5217
  fn = getattr(obj, "to_json", None)