mlrun 1.8.0rc38__py3-none-any.whl → 1.8.0rc40__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 (36) hide show
  1. mlrun/__main__.py +1 -8
  2. mlrun/artifacts/base.py +3 -3
  3. mlrun/artifacts/manager.py +1 -1
  4. mlrun/common/schemas/client_spec.py +1 -0
  5. mlrun/common/schemas/model_monitoring/constants.py +2 -4
  6. mlrun/common/schemas/model_monitoring/model_endpoints.py +5 -11
  7. mlrun/config.py +5 -4
  8. mlrun/datastore/base.py +0 -11
  9. mlrun/db/httpdb.py +11 -0
  10. mlrun/feature_store/api.py +3 -0
  11. mlrun/model_monitoring/api.py +2 -20
  12. mlrun/model_monitoring/controller.py +19 -3
  13. mlrun/model_monitoring/db/tsdb/base.py +11 -0
  14. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +13 -9
  15. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +83 -7
  16. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +38 -4
  17. mlrun/projects/operations.py +3 -3
  18. mlrun/projects/pipelines.py +5 -0
  19. mlrun/projects/project.py +4 -4
  20. mlrun/run.py +4 -4
  21. mlrun/runtimes/kubejob.py +2 -2
  22. mlrun/runtimes/nuclio/application/application.py +0 -2
  23. mlrun/runtimes/nuclio/function.py +1 -46
  24. mlrun/runtimes/nuclio/serving.py +1 -1
  25. mlrun/runtimes/pod.py +37 -145
  26. mlrun/serving/routers.py +80 -64
  27. mlrun/serving/v2_serving.py +24 -62
  28. mlrun/utils/async_http.py +1 -2
  29. mlrun/utils/helpers.py +1 -2
  30. mlrun/utils/version/version.json +2 -2
  31. {mlrun-1.8.0rc38.dist-info → mlrun-1.8.0rc40.dist-info}/METADATA +4 -4
  32. {mlrun-1.8.0rc38.dist-info → mlrun-1.8.0rc40.dist-info}/RECORD +36 -36
  33. {mlrun-1.8.0rc38.dist-info → mlrun-1.8.0rc40.dist-info}/WHEEL +1 -1
  34. {mlrun-1.8.0rc38.dist-info → mlrun-1.8.0rc40.dist-info}/LICENSE +0 -0
  35. {mlrun-1.8.0rc38.dist-info → mlrun-1.8.0rc40.dist-info}/entry_points.txt +0 -0
  36. {mlrun-1.8.0rc38.dist-info → mlrun-1.8.0rc40.dist-info}/top_level.txt +0 -0
mlrun/__main__.py CHANGED
@@ -17,7 +17,6 @@ import json
17
17
  import pathlib
18
18
  import socket
19
19
  import traceback
20
- import warnings
21
20
  from ast import literal_eval
22
21
  from base64 import b64decode
23
22
  from os import environ, path, remove
@@ -864,14 +863,8 @@ def version():
864
863
  )
865
864
  @click.option("--offset", type=int, default=0, help="byte offset")
866
865
  @click.option("--db", help="api and db service path/url")
867
- @click.option("--watch", "-w", is_flag=True, help="Deprecated. not in use")
868
- def logs(uid, project, offset, db, watch):
866
+ def logs(uid, project, offset, db):
869
867
  """Get or watch task logs"""
870
- if watch:
871
- warnings.warn(
872
- "'--watch' is deprecated in 1.6.0, and will be removed in 1.8.0, "
873
- # TODO: Remove in 1.8.0
874
- )
875
868
  mldb = get_run_db(db or mlconf.dbpath)
876
869
  if mldb.kind == "http":
877
870
  state, _ = mldb.watch_log(uid, project, watch=False, offset=offset)
mlrun/artifacts/base.py CHANGED
@@ -893,7 +893,7 @@ def generate_target_path(item: Artifact, artifact_path, producer):
893
893
  return f"{artifact_path}{item.key}{suffix}"
894
894
 
895
895
 
896
- # TODO: left to support data migration from legacy artifacts to new artifacts. Remove in 1.8.0.
896
+ # TODO: Remove once data migration v5 is obsolete
897
897
  def convert_legacy_artifact_to_new_format(
898
898
  legacy_artifact: dict,
899
899
  ) -> Artifact:
@@ -905,9 +905,9 @@ def convert_legacy_artifact_to_new_format(
905
905
  artifact_tag = legacy_artifact.get("tag", "")
906
906
  if artifact_tag:
907
907
  artifact_key = f"{artifact_key}:{artifact_tag}"
908
- # TODO: remove in 1.8.0
908
+ # TODO: Remove once data migration v5 is obsolete
909
909
  warnings.warn(
910
- f"Converting legacy artifact '{artifact_key}' to new format. This will not be supported in MLRun 1.8.0. "
910
+ f"Converting legacy artifact '{artifact_key}' to new format. This will not be supported in MLRun 1.9.0. "
911
911
  f"Make sure to save the artifact/project in the new format.",
912
912
  FutureWarning,
913
913
  )
@@ -108,7 +108,7 @@ class ArtifactProducer:
108
108
  def dict_to_artifact(struct: dict) -> Artifact:
109
109
  kind = struct.get("kind", "")
110
110
 
111
- # TODO: remove this in 1.8.0
111
+ # TODO: Remove once data migration v5 is obsolete
112
112
  if mlrun.utils.is_legacy_artifact(struct):
113
113
  return mlrun.artifacts.base.convert_legacy_artifact_to_new_format(struct)
114
114
 
@@ -66,3 +66,4 @@ class ClientSpec(pydantic.v1.BaseModel):
66
66
  external_platform_tracking: typing.Optional[dict]
67
67
  alerts_mode: typing.Optional[str]
68
68
  system_id: typing.Optional[str]
69
+ model_endpoint_monitoring_store_prefixes: typing.Optional[dict[str, str]]
@@ -42,12 +42,10 @@ class ModelEndpointSchema(MonitoringStrEnum):
42
42
  # spec
43
43
  FUNCTION_NAME = "function_name"
44
44
  FUNCTION_TAG = "function_tag"
45
- FUNCTION_UID = "function_uid"
46
45
  MODEL_NAME = "model_name"
47
- MODEL_DB_KEY = "model_db_key"
48
- MODEL_TAG = "model_tag"
46
+ MODEL_TAGS = "model_tags"
47
+ MODEL_PATH = "model_path"
49
48
  MODEL_CLASS = "model_class"
50
- MODEL_UID = "model_uid"
51
49
  FEATURE_NAMES = "feature_names"
52
50
  LABEL_NAMES = "label_names"
53
51
  FEATURE_STATS = "feature_stats"
@@ -117,14 +117,13 @@ class ModelEndpointMetadata(ObjectMetadata, ModelEndpointParser):
117
117
 
118
118
 
119
119
  class ModelEndpointSpec(ObjectSpec, ModelEndpointParser):
120
- model_uid: Optional[str] = ""
121
- model_name: Optional[str] = ""
122
- model_db_key: Optional[str] = ""
123
- model_tag: Optional[str] = ""
124
120
  model_class: Optional[str] = ""
125
121
  function_name: Optional[str] = ""
126
122
  function_tag: Optional[str] = ""
127
- function_uid: Optional[str] = ""
123
+ model_path: Optional[str] = ""
124
+ model_name: Optional[str] = ""
125
+ model_tags: Optional[list[str]] = []
126
+ _model_id: Optional[int] = ""
128
127
  feature_names: Optional[list[str]] = []
129
128
  label_names: Optional[list[str]] = []
130
129
  feature_stats: Optional[dict] = {}
@@ -137,12 +136,8 @@ class ModelEndpointSpec(ObjectSpec, ModelEndpointParser):
137
136
  @classmethod
138
137
  def mutable_fields(cls):
139
138
  return [
140
- "model_uid",
141
- "model_name",
142
- "model_db_key",
143
- "model_tag",
139
+ "model_path",
144
140
  "model_class",
145
- "function_uid",
146
141
  "feature_names",
147
142
  "label_names",
148
143
  "children",
@@ -206,7 +201,6 @@ class ModelEndpoint(BaseModel):
206
201
  ModelEndpointSchema.CURRENT_STATS,
207
202
  ModelEndpointSchema.DRIFT_MEASURES,
208
203
  ModelEndpointSchema.FUNCTION_URI,
209
- ModelEndpointSchema.MODEL_URI,
210
204
  }
211
205
  # Initialize a flattened dictionary that will be filled with the model endpoint dictionary attributes
212
206
  flatten_dict = {}
mlrun/config.py CHANGED
@@ -232,6 +232,7 @@ default_config = {
232
232
  "delete_project": "900",
233
233
  "delete_function": "900",
234
234
  "model_endpoint_creation": "600",
235
+ "model_endpoint_tsdb_leftovers": "900",
235
236
  },
236
237
  "runtimes": {"dask": "600"},
237
238
  "push_notifications": "60",
@@ -566,16 +567,16 @@ default_config = {
566
567
  },
567
568
  "application_stream_args": {
568
569
  "v3io": {
569
- "shard_count": 1,
570
+ "shard_count": 4,
570
571
  "retention_period_hours": 24,
571
- "num_workers": 1,
572
+ "num_workers": 4,
572
573
  "min_replicas": 1,
573
574
  "max_replicas": 1,
574
575
  },
575
576
  "kafka": {
576
- "partition_count": 1,
577
+ "partition_count": 4,
577
578
  "replication_factor": 1,
578
- "num_workers": 1,
579
+ "num_workers": 4,
579
580
  "min_replicas": 1,
580
581
  "max_replicas": 1,
581
582
  },
mlrun/datastore/base.py CHANGED
@@ -24,7 +24,6 @@ import pandas as pd
24
24
  import pyarrow
25
25
  import pytz
26
26
  import requests
27
- from deprecated import deprecated
28
27
 
29
28
  import mlrun.config
30
29
  import mlrun.errors
@@ -95,16 +94,6 @@ class DataStore:
95
94
  def uri_to_ipython(endpoint, subpath):
96
95
  return ""
97
96
 
98
- # TODO: remove in 1.8.0
99
- @deprecated(
100
- version="1.8.0",
101
- reason="'get_filesystem()' will be removed in 1.8.0, use "
102
- "'filesystem' property instead",
103
- category=FutureWarning,
104
- )
105
- def get_filesystem(self):
106
- return self.filesystem
107
-
108
97
  @property
109
98
  def filesystem(self) -> Optional[fsspec.AbstractFileSystem]:
110
99
  """return fsspec file system object, if supported"""
mlrun/db/httpdb.py CHANGED
@@ -566,6 +566,17 @@ class HTTPRunDB(RunDBInterface):
566
566
  )
567
567
  config.alerts.mode = server_cfg.get("alerts_mode") or config.alerts.mode
568
568
  config.system_id = server_cfg.get("system_id") or config.system_id
569
+ model_monitoring_store_prefixes = (
570
+ server_cfg.get("model_endpoint_monitoring_store_prefixes") or {}
571
+ )
572
+ for prefix in ["default", "user_space", "monitoring_application"]:
573
+ store_prefix_value = model_monitoring_store_prefixes.get(prefix)
574
+ if server_prefix_value is not None:
575
+ setattr(
576
+ config.model_endpoint_monitoring.store_prefixes,
577
+ prefix,
578
+ store_prefix_value,
579
+ )
569
580
 
570
581
  except Exception as exc:
571
582
  logger.warning(
@@ -708,6 +708,9 @@ def _deploy_ingestion_service_v2(
708
708
  function.metadata.name = function.metadata.name or name
709
709
 
710
710
  function.spec.graph = featureset.spec.graph
711
+ function.spec.graph.engine = (
712
+ "async" if featureset.spec.engine == "storey" else "sync"
713
+ )
711
714
  function.spec.parameters = run_config.parameters
712
715
  function.spec.graph_initializer = (
713
716
  "mlrun.feature_store.ingestion.featureset_initializer"
@@ -118,8 +118,6 @@ def get_or_create_model_endpoint(
118
118
  model_endpoint_name=model_endpoint_name,
119
119
  function_name=function_name,
120
120
  function_tag=function_tag,
121
- context=context,
122
- sample_set_statistics=sample_set_statistics,
123
121
  monitoring_mode=monitoring_mode,
124
122
  )
125
123
  return model_endpoint
@@ -344,8 +342,6 @@ def _generate_model_endpoint(
344
342
  model_endpoint_name: str,
345
343
  function_name: str,
346
344
  function_tag: str,
347
- context: "mlrun.MLClientCtx",
348
- sample_set_statistics: dict[str, typing.Any],
349
345
  monitoring_mode: mm_constants.ModelMonitoringMode = mm_constants.ModelMonitoringMode.enabled,
350
346
  ) -> ModelEndpoint:
351
347
  """
@@ -358,21 +354,10 @@ def _generate_model_endpoint(
358
354
  :param model_endpoint_name: Model endpoint name will be presented under the new model endpoint.
359
355
  :param function_name: If a new model endpoint is created, use this function name.
360
356
  :param function_tag: If a new model endpoint is created, use this function tag.
361
- :param context: MLRun context. If function_name not provided, use the context to generate the
362
- full function hash.
363
- :param sample_set_statistics: Dictionary of sample set statistics that will be used as a reference data for
364
- the current model endpoint. Will be stored under
365
- `model_endpoint.status.feature_stats`.
357
+ :param monitoring_mode: Monitoring mode of the new model endpoint.
366
358
 
367
359
  :return `mlrun.common.schemas.ModelEndpoint` object.
368
360
  """
369
- model_obj = None
370
- if model_path:
371
- model_obj: mlrun.artifacts.ModelArtifact = (
372
- mlrun.datastore.store_resources.get_store_resource(
373
- model_path, db=db_session
374
- )
375
- )
376
361
  current_time = datetime_now()
377
362
  model_endpoint = mlrun.common.schemas.ModelEndpoint(
378
363
  metadata=mlrun.common.schemas.ModelEndpointMetadata(
@@ -383,10 +368,7 @@ def _generate_model_endpoint(
383
368
  spec=mlrun.common.schemas.ModelEndpointSpec(
384
369
  function_name=function_name or "function",
385
370
  function_tag=function_tag or "latest",
386
- model_name=model_obj.metadata.key if model_obj else None,
387
- model_uid=model_obj.metadata.uid if model_obj else None,
388
- model_tag=model_obj.metadata.tag if model_obj else None,
389
- model_db_key=model_obj.spec.db_key if model_obj else None,
371
+ model_path=model_path,
390
372
  model_class="drift-analysis",
391
373
  ),
392
374
  status=mlrun.common.schemas.ModelEndpointStatus(
@@ -16,6 +16,7 @@ import concurrent.futures
16
16
  import datetime
17
17
  import json
18
18
  import os
19
+ import traceback
19
20
  from collections.abc import Iterator
20
21
  from contextlib import AbstractContextManager
21
22
  from types import TracebackType
@@ -499,7 +500,7 @@ class MonitoringApplicationController:
499
500
  app_name=app_name,
500
501
  app_stream_type=str(type(app_stream)),
501
502
  )
502
- app_stream.push([data])
503
+ app_stream.push([data], partition_key=endpoint_id)
503
504
 
504
505
  def push_regular_event_to_controller_stream(self) -> None:
505
506
  """
@@ -551,14 +552,29 @@ class MonitoringApplicationController:
551
552
  with concurrent.futures.ThreadPoolExecutor(
552
553
  max_workers=min(len(endpoints), 10)
553
554
  ) as pool:
554
- for endpoint in endpoints:
555
+ futures = {
555
556
  pool.submit(
556
557
  MonitoringApplicationController.endpoint_to_regular_event,
557
558
  endpoint,
558
559
  policy,
559
560
  set(applications_names),
560
561
  self.v3io_access_key,
561
- )
562
+ ): endpoint
563
+ for endpoint in endpoints
564
+ }
565
+ for future in concurrent.futures.as_completed(futures):
566
+ if future.exception():
567
+ exception = future.exception()
568
+ error = (
569
+ f"Failed to push event. Endpoint name: {futures[future].metadata.name}, "
570
+ f"endpoint uid: {futures[future].metadata.uid}, traceback:\n"
571
+ )
572
+ error += "".join(
573
+ traceback.format_exception(
574
+ None, exception, exception.__traceback__
575
+ )
576
+ )
577
+ logger.error(error)
562
578
  logger.info("Finishing monitoring controller chief")
563
579
 
564
580
  @staticmethod
@@ -80,6 +80,17 @@ class TSDBConnector(ABC):
80
80
  :raise mlrun.errors.MLRunRuntimeError: If an error occurred while writing the event.
81
81
  """
82
82
 
83
+ @abstractmethod
84
+ def delete_tsdb_records(
85
+ self, endpoint_ids: list[str], delete_timeout: Optional[int] = None
86
+ ) -> None:
87
+ """
88
+ Delete model endpoint records from the TSDB connector.
89
+ :param endpoint_ids: List of model endpoint unique identifiers.
90
+ :param delete_timeout: The timeout in seconds to wait for the deletion to complete.
91
+ """
92
+ pass
93
+
83
94
  @abstractmethod
84
95
  def delete_tsdb_resources(self):
85
96
  """
@@ -122,26 +122,30 @@ class TDEngineSchema:
122
122
  )
123
123
  return f"DELETE FROM {self.database}.{subtable} WHERE {values};"
124
124
 
125
- def _drop_subtable_query(
125
+ def drop_subtable_query(
126
126
  self,
127
127
  subtable: str,
128
128
  ) -> str:
129
- return f"DROP TABLE if EXISTS {self.database}.{subtable};"
129
+ return f"DROP TABLE if EXISTS {self.database}.`{subtable}`;"
130
130
 
131
131
  def drop_supertable_query(self) -> str:
132
132
  return f"DROP STABLE if EXISTS {self.database}.{self.super_table};"
133
133
 
134
- def _get_subtables_query(
134
+ def _get_subtables_query_by_tag(
135
135
  self,
136
- values: dict[str, Union[str, int, float, datetime.datetime]],
136
+ filter_tag: str,
137
+ filter_values: list[str],
138
+ operator: str = "OR",
137
139
  ) -> str:
138
- values = " AND ".join(
139
- f"{val} LIKE '{values[val]}'" for val in self.tags if val in values
140
- )
141
- if not values:
140
+ if filter_tag not in self.tags:
142
141
  raise mlrun.errors.MLRunInvalidArgumentError(
143
- f"values must contain at least one tag: {self.tags.keys()}"
142
+ f"`filter_tag` must be one of the tags: {self.tags.keys()}"
144
143
  )
144
+
145
+ values = f" {operator} ".join(
146
+ f"{filter_tag} LIKE '{val}'" for val in filter_values
147
+ )
148
+
145
149
  return f"SELECT DISTINCT tbname FROM {self.database}.{self.super_table} WHERE {values};"
146
150
 
147
151
  @staticmethod
@@ -75,13 +75,8 @@ class TDEngineConnector(TSDBConnector):
75
75
  """Establish a connection to the TSDB server."""
76
76
  logger.debug("Creating a new connection to TDEngine", project=self.project)
77
77
  conn = TDEngineConnection(self._tdengine_connection_profile.dsn())
78
- conn.run(
79
- statements=f"CREATE DATABASE IF NOT EXISTS {self.database}",
80
- timeout=self._timeout,
81
- retries=self._retries,
82
- )
83
78
  conn.prefix_statements = [f"USE {self.database}"]
84
- logger.debug("Connected to TDEngine", project=self.project)
79
+
85
80
  return conn
86
81
 
87
82
  def _init_super_tables(self):
@@ -101,8 +96,27 @@ class TDEngineConnector(TSDBConnector):
101
96
  ),
102
97
  }
103
98
 
99
+ def _create_db_if_not_exists(self):
100
+ """Create the database if it does not exist."""
101
+ self.connection.prefix_statements = []
102
+ self.connection.run(
103
+ statements=f"CREATE DATABASE IF NOT EXISTS {self.database}",
104
+ timeout=self._timeout,
105
+ retries=self._retries,
106
+ )
107
+ self.connection.prefix_statements = [f"USE {self.database}"]
108
+ logger.debug(
109
+ "The TDEngine database is currently in use",
110
+ project=self.project,
111
+ database=self.database,
112
+ )
113
+
104
114
  def create_tables(self):
105
115
  """Create TDEngine supertables."""
116
+
117
+ # Create the database if it does not exist
118
+ self._create_db_if_not_exists()
119
+
106
120
  for table in self.tables:
107
121
  create_table_query = self.tables[table]._create_super_table_query()
108
122
  conn = self.connection
@@ -272,6 +286,67 @@ class TDEngineConnector(TSDBConnector):
272
286
  flush_after_seconds=tsdb_batching_timeout_secs,
273
287
  )
274
288
 
289
+ def delete_tsdb_records(
290
+ self, endpoint_ids: list[str], delete_timeout: Optional[int] = None
291
+ ):
292
+ """
293
+ To delete subtables within TDEngine, we first query the subtables names with the provided endpoint_ids.
294
+ Then, we drop each subtable.
295
+ """
296
+ logger.debug(
297
+ "Deleting model endpoint resources using the TDEngine connector",
298
+ project=self.project,
299
+ number_of_endpoints_to_delete=len(endpoint_ids),
300
+ )
301
+
302
+ # Get all subtables with the provided endpoint_ids
303
+ subtables = []
304
+ try:
305
+ for table in self.tables:
306
+ get_subtable_query = self.tables[table]._get_subtables_query_by_tag(
307
+ filter_tag="endpoint_id", filter_values=endpoint_ids
308
+ )
309
+ subtables_result = self.connection.run(
310
+ query=get_subtable_query,
311
+ timeout=self._timeout,
312
+ retries=self._retries,
313
+ )
314
+ subtables.extend([subtable[0] for subtable in subtables_result.data])
315
+ except Exception as e:
316
+ logger.warning(
317
+ "Failed to get subtables for deletion. You may need to delete them manually."
318
+ "These can be found under the following supertables: app_results, "
319
+ "metrics, errors, and predictions.",
320
+ project=self.project,
321
+ error=mlrun.errors.err_to_str(e),
322
+ )
323
+
324
+ # Prepare the drop statements
325
+ drop_statements = []
326
+ for subtable in subtables:
327
+ drop_statements.append(
328
+ self.tables[table].drop_subtable_query(subtable=subtable)
329
+ )
330
+ try:
331
+ self.connection.run(
332
+ statements=drop_statements,
333
+ timeout=delete_timeout or self._timeout,
334
+ retries=self._retries,
335
+ )
336
+ except Exception as e:
337
+ logger.warning(
338
+ "Failed to delete model endpoint resources. You may need to delete them manually. "
339
+ "These can be found under the following supertables: app_results, "
340
+ "metrics, errors, and predictions.",
341
+ project=self.project,
342
+ error=mlrun.errors.err_to_str(e),
343
+ )
344
+ logger.debug(
345
+ "Deleted all model endpoint resources using the TDEngine connector",
346
+ project=self.project,
347
+ number_of_endpoints_to_delete=len(endpoint_ids),
348
+ )
349
+
275
350
  def delete_tsdb_resources(self):
276
351
  """
277
352
  Delete all project resources in the TSDB connector, such as model endpoints data and drift results.
@@ -294,7 +369,7 @@ class TDEngineConnector(TSDBConnector):
294
369
  logger.warning(
295
370
  "Failed to drop TDEngine tables. You may need to drop them manually. "
296
371
  "These can be found under the following supertables: app_results, "
297
- "metrics, and predictions.",
372
+ "metrics, errors, and predictions.",
298
373
  project=self.project,
299
374
  error=mlrun.errors.err_to_str(e),
300
375
  )
@@ -344,6 +419,7 @@ class TDEngineConnector(TSDBConnector):
344
419
  project=self.project,
345
420
  database=self.database,
346
421
  )
422
+
347
423
  except Exception as e:
348
424
  logger.warning(
349
425
  "Failed to drop the database. You may need to drop it manually if it is empty.",
@@ -428,6 +428,40 @@ class V3IOTSDBConnector(TSDBConnector):
428
428
  store, _, _ = mlrun.store_manager.get_or_create_store(tsdb_path)
429
429
  store.rm(tsdb_path, recursive=True)
430
430
 
431
+ def delete_tsdb_records(
432
+ self, endpoint_ids: list[str], delete_timeout: Optional[int] = None
433
+ ):
434
+ logger.debug(
435
+ "Deleting model endpoints resources using the V3IO TSDB connector",
436
+ project=self.project,
437
+ number_of_endpoints_to_delete=len(endpoint_ids),
438
+ )
439
+ tables = mm_schemas.V3IOTSDBTables.list()
440
+
441
+ # Split the endpoint ids into chunks to avoid exceeding the v3io-engine filter-expression limit
442
+ for i in range(0, len(endpoint_ids), V3IO_MEPS_LIMIT):
443
+ endpoint_id_chunk = endpoint_ids[i : i + V3IO_MEPS_LIMIT]
444
+ filter_query = f"endpoint_id IN({str(endpoint_id_chunk)[1:-1]}) "
445
+ for table in tables:
446
+ try:
447
+ self.frames_client.delete(
448
+ backend=_TSDB_BE,
449
+ table=self.tables[table],
450
+ filter=filter_query,
451
+ start="0",
452
+ )
453
+ except Exception as e:
454
+ logger.warning(
455
+ f"Failed to delete TSDB records for the provided endpoints from table '{table}'",
456
+ error=mlrun.errors.err_to_str(e),
457
+ project=self.project,
458
+ )
459
+ logger.debug(
460
+ "Deleted all model endpoint resources using the V3IO connector",
461
+ project=self.project,
462
+ number_of_endpoints_to_delete=len(endpoint_ids),
463
+ )
464
+
431
465
  def get_model_endpoint_real_time_metrics(
432
466
  self, endpoint_id: str, metrics: list[str], start: str, end: str
433
467
  ) -> dict[str, list[tuple[str, float]]]:
@@ -1052,12 +1086,12 @@ class V3IOTSDBConnector(TSDBConnector):
1052
1086
  )
1053
1087
  add_metric(
1054
1088
  "avg_latency",
1055
- "max(result_status)",
1056
- drift_status_res,
1089
+ "avg(latency)",
1090
+ avg_latency_res,
1057
1091
  )
1058
1092
  add_metric(
1059
1093
  "result_status",
1060
- "avg(latency)",
1061
- avg_latency_res,
1094
+ "max(result_status)",
1095
+ drift_status_res,
1062
1096
  )
1063
1097
  return list(model_endpoint_objects_by_uid.values())
@@ -294,9 +294,9 @@ def build_function(
294
294
  :param force_build: Force building the image, even when no changes were made
295
295
  """
296
296
  if not overwrite_build_params:
297
- # TODO: change overwrite_build_params default to True in 1.8.0
297
+ # TODO: change overwrite_build_params default to True in 1.9.0
298
298
  warnings.warn(
299
- "The `overwrite_build_params` parameter default will change from 'False' to 'True' in 1.8.0.",
299
+ "The `overwrite_build_params` parameter default will change from 'False' to 'True' in 1.9.0.",
300
300
  mlrun.utils.OverwriteBuildParamsWarning,
301
301
  )
302
302
 
@@ -325,7 +325,7 @@ def build_function(
325
325
  skip_deployed=skip_deployed,
326
326
  )
327
327
  else:
328
- # TODO: remove filter once overwrite_build_params default is changed to True in 1.8.0
328
+ # TODO: remove filter once overwrite_build_params default is changed to True in 1.9.0
329
329
  with warnings.catch_warnings():
330
330
  warnings.simplefilter(
331
331
  "ignore", category=mlrun.utils.OverwriteBuildParamsWarning
@@ -1139,6 +1139,11 @@ def load_and_run_workflow(
1139
1139
  if "running" in notification.when
1140
1140
  ]
1141
1141
 
1142
+ # Prevent redundant notifications for run completion by ensuring that notifications are only triggered when the run
1143
+ # reaches the "running" state, as the server already handles the completion notifications.
1144
+ for notification in start_notifications:
1145
+ notification.when = ["running"]
1146
+
1142
1147
  workflow_log_message = workflow_name or workflow_path
1143
1148
  context.logger.info(f"Running workflow {workflow_log_message} from remote")
1144
1149
  run = project.run(
mlrun/projects/project.py CHANGED
@@ -4059,9 +4059,9 @@ class MlrunProject(ModelObj):
4059
4059
  (by default `/home/mlrun_code`)
4060
4060
  """
4061
4061
  if not overwrite_build_params:
4062
- # TODO: change overwrite_build_params default to True in 1.8.0
4062
+ # TODO: change overwrite_build_params default to True in 1.9.0
4063
4063
  warnings.warn(
4064
- "The `overwrite_build_params` parameter default will change from 'False' to 'True' in 1.8.0.",
4064
+ "The `overwrite_build_params` parameter default will change from 'False' to 'True' in 1.9.0.",
4065
4065
  mlrun.utils.OverwriteBuildParamsWarning,
4066
4066
  )
4067
4067
  default_image_name = mlrun.mlconf.default_project_image_name.format(
@@ -4136,9 +4136,9 @@ class MlrunProject(ModelObj):
4136
4136
  )
4137
4137
 
4138
4138
  if not overwrite_build_params:
4139
- # TODO: change overwrite_build_params default to True in 1.8.0
4139
+ # TODO: change overwrite_build_params default to True in 1.9.0
4140
4140
  warnings.warn(
4141
- "The `overwrite_build_params` parameter default will change from 'False' to 'True' in 1.8.0.",
4141
+ "The `overwrite_build_params` parameter default will change from 'False' to 'True' in 1.9.0.",
4142
4142
  mlrun.utils.OverwriteBuildParamsWarning,
4143
4143
  )
4144
4144
 
mlrun/run.py CHANGED
@@ -36,9 +36,9 @@ import mlrun.common.formatters
36
36
  import mlrun.common.schemas
37
37
  import mlrun.errors
38
38
  import mlrun.utils.helpers
39
+ import mlrun_pipelines.utils
39
40
  from mlrun_pipelines.common.models import RunStatuses
40
41
  from mlrun_pipelines.common.ops import format_summary_from_kfp_run, show_kfp_run
41
- from mlrun_pipelines.utils import get_client
42
42
 
43
43
  from .common.helpers import parse_versioned_object_uri
44
44
  from .config import config as mlconf
@@ -437,7 +437,7 @@ def new_function(
437
437
  mode: Optional[str] = None,
438
438
  handler: Optional[str] = None,
439
439
  source: Optional[str] = None,
440
- requirements: Optional[Union[str, list[str]]] = None,
440
+ requirements: Optional[list[str]] = None,
441
441
  kfp: Optional[bool] = None,
442
442
  requirements_file: str = "",
443
443
  ):
@@ -1015,7 +1015,7 @@ def wait_for_pipeline_completion(
1015
1015
  _wait_for_pipeline_completion,
1016
1016
  )
1017
1017
  else:
1018
- client = get_client(namespace=namespace)
1018
+ client = mlrun_pipelines.utils.get_client(namespace=namespace)
1019
1019
  resp = client.wait_for_run_completion(run_id, timeout)
1020
1020
  if resp:
1021
1021
  resp = resp.to_dict()
@@ -1076,7 +1076,7 @@ def get_pipeline(
1076
1076
  )
1077
1077
 
1078
1078
  else:
1079
- client = get_client(namespace=namespace)
1079
+ client = mlrun_pipelines.utils.get_client(namespace=namespace)
1080
1080
  resp = client.get_run(run_id)
1081
1081
  if resp:
1082
1082
  resp = resp.to_dict()
mlrun/runtimes/kubejob.py CHANGED
@@ -114,9 +114,9 @@ class KubejobRuntime(KubeResource):
114
114
  e.g. builder_env={"GIT_TOKEN": token}
115
115
  """
116
116
  if not overwrite:
117
- # TODO: change overwrite default to True in 1.8.0
117
+ # TODO: change overwrite default to True in 1.9.0
118
118
  warnings.warn(
119
- "The `overwrite` parameter default will change from 'False' to 'True' in 1.8.0.",
119
+ "The `overwrite` parameter default will change from 'False' to 'True' in 1.9.0.",
120
120
  mlrun.utils.OverwriteBuildParamsWarning,
121
121
  )
122
122
  image = mlrun.utils.helpers.remove_image_protocol_prefix(image)