mlrun 1.8.0rc45__py3-none-any.whl → 1.8.0rc47__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.

@@ -13,52 +13,38 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import json
16
+ from abc import ABC, abstractmethod
16
17
  from contextlib import AbstractContextManager
17
18
  from types import TracebackType
18
19
  from typing import Final, Optional
19
20
 
20
21
  import botocore.exceptions
21
22
 
22
- import mlrun.common.schemas
23
+ import mlrun.common.schemas as schemas
23
24
  import mlrun.errors
24
25
  import mlrun.model_monitoring.helpers
25
26
  from mlrun.utils import logger
26
27
 
27
28
 
28
- class ModelMonitoringSchedulesFile(AbstractContextManager):
29
+ class ModelMonitoringSchedulesFileBase(AbstractContextManager, ABC):
29
30
  DEFAULT_SCHEDULES: Final = {}
30
31
  INITIAL_CONTENT = json.dumps(DEFAULT_SCHEDULES)
31
32
  ENCODING = "utf-8"
32
33
 
33
- def __init__(self, project: str, endpoint_id: str) -> None:
34
- """
35
- Initialize applications monitoring schedules file object.
36
- The JSON file stores a dictionary of registered application name as key and Unix timestamp as value.
37
- When working with the schedules data, use this class as a context manager to read and write the data.
38
-
39
- :param project: The project name.
40
- :param endpoint_id: The endpoint ID.
41
- """
42
- # `self._item` is the persistent version of the monitoring schedules.
43
- self._item = mlrun.model_monitoring.helpers.get_monitoring_schedules_data(
44
- project=project, endpoint_id=endpoint_id
45
- )
46
- self._path = self._item.url
47
- self._fs = self._item.store.filesystem
48
- # `self._schedules` is an in-memory copy of the DB for all the applications for
49
- # the same model endpoint.
50
- self._schedules: dict[str, int] = self.DEFAULT_SCHEDULES.copy()
51
- # Does `self._schedules` hold the content of `self._item`?
52
- self._open_schedules = False
53
-
54
- @classmethod
55
- def from_model_endpoint(
56
- cls, model_endpoint: mlrun.common.schemas.ModelEndpoint
57
- ) -> "ModelMonitoringSchedulesFile":
58
- return cls(
59
- project=model_endpoint.metadata.project,
60
- endpoint_id=model_endpoint.metadata.uid,
61
- )
34
+ def __init__(self):
35
+ self._item = self.get_data_item_object()
36
+ if self._item:
37
+ self._path = self._item.url
38
+ self._fs = self._item.store.filesystem
39
+ # `self._schedules` is an in-memory copy of the DB for all the applications for
40
+ # the same model endpoint.
41
+ self._schedules = self.DEFAULT_SCHEDULES.copy()
42
+ # Does `self._schedules` hold the content of `self._item`?
43
+ self._open_schedules = False
44
+
45
+ @abstractmethod
46
+ def get_data_item_object(self) -> mlrun.DataItem:
47
+ pass
62
48
 
63
49
  def create(self) -> None:
64
50
  """Create a schedules file with initial content - an empty dictionary"""
@@ -114,7 +100,7 @@ class ModelMonitoringSchedulesFile(AbstractContextManager):
114
100
  self._schedules = self.DEFAULT_SCHEDULES
115
101
  self._open_schedules = False
116
102
 
117
- def __enter__(self) -> "ModelMonitoringSchedulesFile":
103
+ def __enter__(self) -> "ModelMonitoringSchedulesFileBase":
118
104
  self._open()
119
105
  return super().__enter__()
120
106
 
@@ -132,6 +118,36 @@ class ModelMonitoringSchedulesFile(AbstractContextManager):
132
118
  "Open the schedules file as a context manager first"
133
119
  )
134
120
 
121
+
122
+ class ModelMonitoringSchedulesFileEndpoint(ModelMonitoringSchedulesFileBase):
123
+ def __init__(self, project: str, endpoint_id: str) -> None:
124
+ """
125
+ Initialize applications monitoring schedules file object.
126
+ The JSON file stores a dictionary of registered application name as key and Unix timestamp as value.
127
+ When working with the schedules data, use this class as a context manager to read and write the data.
128
+
129
+ :param project: The project name.
130
+ :param endpoint_id: The endpoint ID.
131
+ """
132
+ # `self._item` is the persistent version of the monitoring schedules.
133
+ self._project = project
134
+ self._endpoint_id = endpoint_id
135
+ super().__init__()
136
+
137
+ def get_data_item_object(self) -> mlrun.DataItem:
138
+ return mlrun.model_monitoring.helpers.get_monitoring_schedules_endpoint_data(
139
+ project=self._project, endpoint_id=self._endpoint_id
140
+ )
141
+
142
+ @classmethod
143
+ def from_model_endpoint(
144
+ cls, model_endpoint: schemas.ModelEndpoint
145
+ ) -> "ModelMonitoringSchedulesFileEndpoint":
146
+ return cls(
147
+ project=model_endpoint.metadata.project,
148
+ endpoint_id=model_endpoint.metadata.uid,
149
+ )
150
+
135
151
  def get_application_time(self, application: str) -> Optional[int]:
136
152
  self._check_open_schedules()
137
153
  return self._schedules.get(application)
@@ -149,6 +165,68 @@ class ModelMonitoringSchedulesFile(AbstractContextManager):
149
165
  return min(self._schedules.values(), default=None)
150
166
 
151
167
 
168
+ class ModelMonitoringSchedulesFileChief(ModelMonitoringSchedulesFileBase):
169
+ def __init__(self, project: str) -> None:
170
+ """
171
+ Initialize applications monitoring schedules chief file object.
172
+ The JSON file stores a dictionary of registered model endpoints uid as key and point to a dictionary of
173
+ "last_request" and "last_analyzed" mapped to two Unix timestamps as values.
174
+ When working with the schedules data, use this class as a context manager to read and write the data.
175
+
176
+ :param project: The project name.
177
+ """
178
+ # `self._item` is the persistent version of the monitoring schedules.
179
+ self._project = project
180
+ super().__init__()
181
+
182
+ def get_data_item_object(self) -> mlrun.DataItem:
183
+ return mlrun.model_monitoring.helpers.get_monitoring_schedules_chief_data(
184
+ project=self._project
185
+ )
186
+
187
+ def get_endpoint_last_request(self, endpoint_uid: str) -> Optional[int]:
188
+ self._check_open_schedules()
189
+ if endpoint_uid in self._schedules:
190
+ return self._schedules[endpoint_uid].get(
191
+ schemas.model_monitoring.constants.ScheduleChiefFields.LAST_REQUEST
192
+ )
193
+ else:
194
+ return None
195
+
196
+ def update_endpoint_timestamps(
197
+ self, endpoint_uid: str, last_request: int, last_analyzed: int
198
+ ) -> None:
199
+ self._check_open_schedules()
200
+ self._schedules[endpoint_uid] = {
201
+ schemas.model_monitoring.constants.ScheduleChiefFields.LAST_REQUEST: last_request,
202
+ schemas.model_monitoring.constants.ScheduleChiefFields.LAST_ANALYZED: last_analyzed,
203
+ }
204
+
205
+ def get_endpoint_last_analyzed(self, endpoint_uid: str) -> Optional[int]:
206
+ self._check_open_schedules()
207
+ if endpoint_uid in self._schedules:
208
+ return self._schedules[endpoint_uid].get(
209
+ schemas.model_monitoring.constants.ScheduleChiefFields.LAST_ANALYZED
210
+ )
211
+ else:
212
+ return None
213
+
214
+ def get_endpoint_list(self) -> set[str]:
215
+ self._check_open_schedules()
216
+ return set(self._schedules.keys())
217
+
218
+ def get_or_create(self) -> None:
219
+ try:
220
+ self._open()
221
+ except (
222
+ mlrun.errors.MLRunNotFoundError,
223
+ # Different errors are raised for S3 or local storage, see ML-8042
224
+ botocore.exceptions.ClientError,
225
+ FileNotFoundError,
226
+ ):
227
+ self.create()
228
+
229
+
152
230
  def delete_model_monitoring_schedules_folder(project: str) -> None:
153
231
  """Delete the model monitoring schedules folder of the project"""
154
232
  folder = mlrun.model_monitoring.helpers._get_monitoring_schedules_folder_path(
@@ -55,6 +55,9 @@ class TDEngineConnector(TSDBConnector):
55
55
 
56
56
  self._init_super_tables()
57
57
 
58
+ self._run_directly = (
59
+ mlrun.mlconf.model_endpoint_monitoring.tdengine.run_directly
60
+ )
58
61
  self._timeout = mlrun.mlconf.model_endpoint_monitoring.tdengine.timeout
59
62
  self._retries = mlrun.mlconf.model_endpoint_monitoring.tdengine.retries
60
63
 
@@ -74,7 +77,9 @@ class TDEngineConnector(TSDBConnector):
74
77
  def _create_connection(self) -> TDEngineConnection:
75
78
  """Establish a connection to the TSDB server."""
76
79
  logger.debug("Creating a new connection to TDEngine", project=self.project)
77
- conn = TDEngineConnection(self._tdengine_connection_profile.dsn())
80
+ conn = TDEngineConnection(
81
+ self._tdengine_connection_profile.dsn(), run_directly=self._run_directly
82
+ )
78
83
  conn.prefix_statements = [f"USE {self.database}"]
79
84
 
80
85
  return conn
@@ -1090,9 +1090,9 @@ class V3IOTSDBConnector(TSDBConnector):
1090
1090
  Fetch basic metrics from V3IO TSDB and add them to MEP objects.
1091
1091
 
1092
1092
  :param model_endpoint_objects: A list of `ModelEndpoint` objects that will
1093
- be filled with the relevant basic metrics.
1093
+ be filled with the relevant basic metrics.
1094
1094
  :param project: The name of the project.
1095
- :param run_in_threadpool: Has no effect.
1095
+ :param run_in_threadpool: A function that runs another function in a thread pool.
1096
1096
 
1097
1097
  :return: A list of `ModelEndpointMonitoringMetric` objects.
1098
1098
  """
@@ -1104,9 +1104,15 @@ class V3IOTSDBConnector(TSDBConnector):
1104
1104
  uids.append(uid)
1105
1105
  model_endpoint_objects_by_uid[uid] = model_endpoint_object
1106
1106
 
1107
- error_count_res = self.get_error_count(endpoint_ids=uids, get_raw=True)
1108
- avg_latency_res = self.get_avg_latency(endpoint_ids=uids, get_raw=True)
1109
- drift_status_res = self.get_drift_status(endpoint_ids=uids, get_raw=True)
1107
+ error_count_res = await run_in_threadpool(
1108
+ self.get_error_count, endpoint_ids=uids, get_raw=True
1109
+ )
1110
+ avg_latency_res = await run_in_threadpool(
1111
+ self.get_avg_latency, endpoint_ids=uids, get_raw=True
1112
+ )
1113
+ drift_status_res = await run_in_threadpool(
1114
+ self.get_drift_status, endpoint_ids=uids, get_raw=True
1115
+ )
1110
1116
 
1111
1117
  def add_metric(
1112
1118
  metric: str,
@@ -432,57 +432,23 @@ def update_model_endpoint_last_request(
432
432
  :param current_request: current request time
433
433
  :param db: DB interface.
434
434
  """
435
- is_batch_endpoint = (
436
- model_endpoint.metadata.endpoint_type == mm_constants.EndpointType.BATCH_EP
437
- )
438
- if not is_batch_endpoint:
439
- logger.info(
440
- "Update model endpoint last request time (EP with serving)",
441
- project=project,
442
- endpoint_id=model_endpoint.metadata.uid,
443
- name=model_endpoint.metadata.name,
444
- function_name=model_endpoint.spec.function_name,
445
- last_request=model_endpoint.status.last_request,
446
- current_request=current_request,
447
- )
448
- db.patch_model_endpoint(
449
- project=project,
450
- endpoint_id=model_endpoint.metadata.uid,
451
- name=model_endpoint.metadata.name,
452
- attributes={mm_constants.EventFieldType.LAST_REQUEST: current_request},
453
- )
454
- else: # model endpoint without any serving function - close the window "manually"
455
- try:
456
- time_window = _get_monitoring_time_window_from_controller_run(project, db)
457
- except mlrun.errors.MLRunNotFoundError:
458
- logger.warn(
459
- "Not bumping model endpoint last request time - the monitoring controller isn't deployed yet.\n"
460
- "Call `project.enable_model_monitoring()` first."
461
- )
462
- return
463
435
 
464
- bumped_last_request = (
465
- current_request
466
- + time_window
467
- + datetime.timedelta(
468
- seconds=mlrun.mlconf.model_endpoint_monitoring.parquet_batching_timeout_secs
469
- )
470
- )
471
- logger.info(
472
- "Bumping model endpoint last request time (EP without serving)",
473
- project=project,
474
- endpoint_id=model_endpoint.metadata.uid,
475
- last_request=model_endpoint.status.last_request,
476
- current_request=current_request.isoformat(),
477
- bumped_last_request=bumped_last_request,
478
- )
479
- db.patch_model_endpoint(
480
- project=project,
481
- endpoint_id=model_endpoint.metadata.uid,
482
- name=model_endpoint.metadata.name,
483
- function_name=model_endpoint.spec.function_name,
484
- attributes={mm_constants.EventFieldType.LAST_REQUEST: bumped_last_request},
485
- )
436
+ logger.info(
437
+ "Update model endpoint last request time (EP with serving)",
438
+ project=project,
439
+ endpoint_id=model_endpoint.metadata.uid,
440
+ name=model_endpoint.metadata.name,
441
+ function_name=model_endpoint.spec.function_name,
442
+ last_request=model_endpoint.status.last_request,
443
+ current_request=current_request,
444
+ )
445
+ db.patch_model_endpoint(
446
+ project=project,
447
+ endpoint_id=model_endpoint.metadata.uid,
448
+ name=model_endpoint.metadata.name,
449
+ function_name=model_endpoint.spec.function_name,
450
+ attributes={mm_constants.EventFieldType.LAST_REQUEST: current_request},
451
+ )
486
452
 
487
453
 
488
454
  def calculate_inputs_statistics(
@@ -586,16 +552,43 @@ def _get_monitoring_schedules_folder_path(project: str) -> str:
586
552
  )
587
553
 
588
554
 
589
- def _get_monitoring_schedules_file_path(*, project: str, endpoint_id: str) -> str:
555
+ def _get_monitoring_schedules_file_endpoint_path(
556
+ *, project: str, endpoint_id: str
557
+ ) -> str:
590
558
  return os.path.join(
591
559
  _get_monitoring_schedules_folder_path(project), f"{endpoint_id}.json"
592
560
  )
593
561
 
594
562
 
595
- def get_monitoring_schedules_data(*, project: str, endpoint_id: str) -> "DataItem":
563
+ def get_monitoring_schedules_endpoint_data(
564
+ *, project: str, endpoint_id: str
565
+ ) -> "DataItem":
596
566
  """
597
567
  Get the model monitoring schedules' data item of the project's model endpoint.
598
568
  """
599
569
  return mlrun.datastore.store_manager.object(
600
- _get_monitoring_schedules_file_path(project=project, endpoint_id=endpoint_id)
570
+ _get_monitoring_schedules_file_endpoint_path(
571
+ project=project, endpoint_id=endpoint_id
572
+ )
573
+ )
574
+
575
+
576
+ def get_monitoring_schedules_chief_data(
577
+ *,
578
+ project: str,
579
+ ) -> "DataItem":
580
+ """
581
+ Get the model monitoring schedules' data item of the project's model endpoint.
582
+ """
583
+ return mlrun.datastore.store_manager.object(
584
+ _get_monitoring_schedules_file_chief_path(project=project)
585
+ )
586
+
587
+
588
+ def _get_monitoring_schedules_file_chief_path(
589
+ *,
590
+ project: str,
591
+ ) -> str:
592
+ return os.path.join(
593
+ _get_monitoring_schedules_folder_path(project), f"{project}.json"
601
594
  )
mlrun/projects/project.py CHANGED
@@ -2144,29 +2144,34 @@ class MlrunProject(ModelObj):
2144
2144
  reset_policy: mlrun.common.schemas.alert.ResetPolicy = mlrun.common.schemas.alert.ResetPolicy.AUTO,
2145
2145
  ) -> list[mlrun.alerts.alert.AlertConfig]:
2146
2146
  """
2147
- :param name: The name of the AlertConfig template. It will be combined with mep_id, app-name
2148
- and result name to generate a unique name.
2149
- :param summary: Summary of the alert, will be sent in the generated notifications
2150
- :param endpoints: The endpoints from which metrics will be retrieved to configure the alerts.
2151
- This `ModelEndpointList` object obtained via the `list_model_endpoints`
2152
- method or created manually using `ModelEndpoint` objects.
2153
- :param events: AlertTrigger event types (EventKind).
2154
- :param notifications: List of notifications to invoke once the alert is triggered
2155
- :param result_names: Optional. Filters the result names used to create the alert configuration,
2156
- constructed from the app and result_name regex.
2157
-
2158
- For example:
2159
- [`app1.result-*`, `*.result1`]
2160
- will match "mep_uid1.app1.result.result-1" and "mep_uid1.app2.result.result1".
2161
- A specific result_name (not a wildcard) will always create a new alert
2162
- config, regardless of whether the result name exists.
2163
- :param severity: Severity of the alert.
2164
- :param criteria: When the alert will be triggered based on the
2165
- specified number of events within the defined time period.
2166
- :param reset_policy: When to clear the alert. May be "manual" for manual reset of the alert,
2167
- or "auto" if the criteria contains a time period.
2168
- :returns: List of AlertConfig according to endpoints results,
2169
- filtered by result_names.
2147
+ Generate alert configurations based on specified model endpoints and result names, which can be defined
2148
+ explicitly or using regex patterns.
2149
+
2150
+ :param name: The name of the AlertConfig template. It will be combined with
2151
+ mep id, app name and result name to generate a unique name.
2152
+ :param summary: Summary of the alert, will be sent in the generated notifications
2153
+ :param endpoints: The endpoints from which metrics will be retrieved to configure
2154
+ the alerts.
2155
+ The ModelEndpointList object is obtained via the `list_model_endpoints`
2156
+ method or created manually using `ModelEndpoint` objects.
2157
+ :param events: AlertTrigger event types (EventKind).
2158
+ :param notifications: List of notifications to invoke once the alert is triggered
2159
+ :param result_names: Optional. Filters the result names used to create the alert
2160
+ configuration, constructed from the app and result_name regex.
2161
+
2162
+ For example:
2163
+ [`app1.result-*`, `*.result1`]
2164
+ will match "mep_uid1.app1.result.result-1" and
2165
+ "mep_uid1.app2.result.result1".
2166
+ A specific result_name (not a wildcard) will always create a new alert
2167
+ config, regardless of whether the result name exists.
2168
+ :param severity: Severity of the alert.
2169
+ :param criteria: The threshold for triggering the alert based on the
2170
+ specified number of events within the defined time period.
2171
+ :param reset_policy: When to clear the alert. Either "manual" for manual reset of the alert,
2172
+ or "auto" if the criteria contains a time period.
2173
+ :returns: List of AlertConfig according to endpoints results,
2174
+ filtered by result_names.
2170
2175
  """
2171
2176
  db = mlrun.db.get_run_db(secrets=self._secrets)
2172
2177
  matching_results = []
@@ -5266,7 +5271,7 @@ class MlrunProject(ModelObj):
5266
5271
  )
5267
5272
 
5268
5273
  # if engine is remote then skip the local file validation
5269
- if engine and not engine.startswith("remote"):
5274
+ if engine and engine.startswith("remote"):
5270
5275
  return
5271
5276
 
5272
5277
  code_path = self.spec.get_code_path()
@@ -36,6 +36,7 @@ class FunctionReference(ModelObj):
36
36
  spec=None,
37
37
  kind=None,
38
38
  name=None,
39
+ track_models=None,
39
40
  ):
40
41
  self.url = url
41
42
  self.kind = kind
@@ -46,6 +47,7 @@ class FunctionReference(ModelObj):
46
47
  spec = spec.to_dict()
47
48
  self.spec = spec
48
49
  self.code = code
50
+ self.track_models = track_models
49
51
 
50
52
  self._function = None
51
53
  self._address = None
@@ -130,6 +132,7 @@ class FunctionReference(ModelObj):
130
132
  if self.requirements:
131
133
  func.with_requirements(self.requirements)
132
134
  self._function = func
135
+ func.spec.track_models = self.track_models
133
136
  return func
134
137
 
135
138
  @property
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import asyncio
16
+ import copy
16
17
  import json
17
18
  import typing
18
19
  import warnings
@@ -50,6 +51,19 @@ from mlrun.runtimes.utils import get_item_name, log_std
50
51
  from mlrun.utils import get_in, logger, update_in
51
52
  from mlrun_pipelines.common.ops import deploy_op
52
53
 
54
+ SENSITIVE_PATHS_IN_TRIGGER_CONFIG = {
55
+ "password",
56
+ "secret",
57
+ "attributes/password",
58
+ "attributes/accesskeyid",
59
+ "attributes/secretaccesskey",
60
+ "attributes/cacert",
61
+ "attributes/accesskey",
62
+ "attributes/accesscertificate",
63
+ "attributes/sasl/password",
64
+ "attributes/sasl/oauth/clientsecret",
65
+ }
66
+
53
67
 
54
68
  def validate_nuclio_version_compatibility(*min_versions):
55
69
  """
@@ -274,6 +288,37 @@ class RemoteRuntime(KubeResource):
274
288
  if self.metadata.tag:
275
289
  mlrun.utils.validate_tag_name(self.metadata.tag, "function.metadata.tag")
276
290
 
291
+ def mask_sensitive_data_in_config(self):
292
+ if not self.spec.config:
293
+ return {}
294
+
295
+ raw_config = copy.deepcopy(self.spec.config)
296
+
297
+ for key, value in self.spec.config.items():
298
+ if key.startswith("spec.triggers"):
299
+ trigger_name = key.split(".")[-1]
300
+
301
+ for path in SENSITIVE_PATHS_IN_TRIGGER_CONFIG:
302
+ # Handle nested keys
303
+ nested_keys = path.split("/")
304
+ target = value
305
+ for sub_key in nested_keys[:-1]:
306
+ target = target.get(sub_key, {})
307
+
308
+ last_key = nested_keys[-1]
309
+ if last_key in target:
310
+ sensitive_field = target[last_key]
311
+ if sensitive_field.startswith(
312
+ mlrun.model.Credentials.secret_reference_prefix
313
+ ):
314
+ # already masked
315
+ continue
316
+ target[last_key] = (
317
+ f"{mlrun.model.Credentials.secret_reference_prefix}/spec/triggers/{trigger_name}/{path}"
318
+ )
319
+
320
+ return raw_config
321
+
277
322
  def set_config(self, key, value):
278
323
  self.spec.config[key] = value
279
324
  return self
@@ -1230,6 +1275,9 @@ class RemoteRuntime(KubeResource):
1230
1275
  if remote_env.get("name") in credentials_env_var_names:
1231
1276
  new_env.append(remote_env)
1232
1277
 
1278
+ # update nuclio-specific credentials
1279
+ self.mask_sensitive_data_in_config()
1280
+
1233
1281
  self.spec.env = new_env
1234
1282
 
1235
1283
  def _set_as_mock(self, enable):
@@ -337,6 +337,17 @@ class ServingRuntime(RemoteRuntime):
337
337
  """
338
338
  # Applying model monitoring configurations
339
339
  self.spec.track_models = enable_tracking
340
+ if self._spec and self._spec.function_refs:
341
+ logger.debug(
342
+ "Set tracking for children references", enable_tracking=enable_tracking
343
+ )
344
+ for name in self._spec.function_refs.keys():
345
+ self._spec.function_refs[name].track_models = enable_tracking
346
+ # Check if function_refs _function is filled if so update track_models field:
347
+ if self._spec.function_refs[name]._function:
348
+ self._spec.function_refs[
349
+ name
350
+ ]._function.spec.track_models = enable_tracking
340
351
 
341
352
  if not 0 < sampling_percentage <= 100:
342
353
  raise mlrun.errors.MLRunInvalidArgumentError(
@@ -506,7 +517,11 @@ class ServingRuntime(RemoteRuntime):
506
517
  :return function object
507
518
  """
508
519
  function_reference = FunctionReference(
509
- url, image, requirements=requirements, kind=kind or "serving"
520
+ url,
521
+ image,
522
+ requirements=requirements,
523
+ kind=kind or "serving",
524
+ track_models=self.spec.track_models,
510
525
  )
511
526
  self._spec.function_refs.update(function_reference, name)
512
527
  func = function_reference.to_function(self.kind)
mlrun/serving/states.py CHANGED
@@ -363,15 +363,22 @@ class BaseStep(ModelObj):
363
363
  event: {"x": 5} , result_path="y" means the output of the step will be written
364
364
  to event["y"] resulting in {"x": 5, "y": <result>}
365
365
  :param model_endpoint_creation_strategy: Strategy for creating or updating the model endpoint:
366
- * **overwrite**:
367
- 1. If model endpoints with the same name exist, delete the `latest` one.
368
- 2. Create a new model endpoint entry and set it as `latest`.
369
- * **inplace** (default):
370
- 1. If model endpoints with the same name exist, update the `latest` entry.
371
- 2. Otherwise, create a new entry.
372
- * **archive**:
373
- 1. If model endpoints with the same name exist, preserve them.
374
- 2. Create a new model endpoint with the same name and set it to `latest`.
366
+
367
+ * **overwrite**:
368
+
369
+ 1. If model endpoints with the same name exist, delete the `latest` one.
370
+ 2. Create a new model endpoint entry and set it as `latest`.
371
+
372
+ * **inplace** (default):
373
+
374
+ 1. If model endpoints with the same name exist, update the `latest` entry.
375
+ 2. Otherwise, create a new entry.
376
+
377
+ * **archive**:
378
+
379
+ 1. If model endpoints with the same name exist, preserve them.
380
+ 2. Create a new model endpoint with the same name and set it to `latest`.
381
+
375
382
  :param class_args: class init arguments
376
383
  """
377
384
  if hasattr(self, "steps"):
@@ -810,15 +817,22 @@ class RouterStep(TaskStep):
810
817
  :param handler: class handler to invoke on run/event
811
818
  :param function: function this step should run in
812
819
  :param creation_strategy: Strategy for creating or updating the model endpoint:
813
- * **overwrite**:
814
- 1. If model endpoints with the same name exist, delete the `latest` one.
815
- 2. Create a new model endpoint entry and set it as `latest`.
816
- * **inplace** (default):
817
- 1. If model endpoints with the same name exist, update the `latest` entry.
818
- 2. Otherwise, create a new entry.
819
- * **archive**:
820
- 1. If model endpoints with the same name exist, preserve them.
821
- 2. Create a new model endpoint with the same name and set it to `latest`.
820
+
821
+ * **overwrite**:
822
+
823
+ 1. If model endpoints with the same name exist, delete the `latest` one.
824
+ 2. Create a new model endpoint entry and set it as `latest`.
825
+
826
+ * **inplace** (default):
827
+
828
+ 1. If model endpoints with the same name exist, update the `latest` entry.
829
+ 2. Otherwise, create a new entry.
830
+
831
+ * **archive**:
832
+
833
+ 1. If model endpoints with the same name exist, preserve them.
834
+ 2. Create a new model endpoint with the same name and set it to `latest`.
835
+
822
836
  """
823
837
 
824
838
  if len(self.routes.keys()) >= MAX_MODELS_PER_ROUTER and key not in self.routes:
@@ -1207,15 +1221,22 @@ class FlowStep(BaseStep):
1207
1221
  event: {"x": 5} , result_path="y" means the output of the step will be written
1208
1222
  to event["y"] resulting in {"x": 5, "y": <result>}
1209
1223
  :param model_endpoint_creation_strategy: Strategy for creating or updating the model endpoint:
1210
- * **overwrite**:
1211
- 1. If model endpoints with the same name exist, delete the `latest` one.
1212
- 2. Create a new model endpoint entry and set it as `latest`.
1213
- * **inplace** (default):
1214
- 1. If model endpoints with the same name exist, update the `latest` entry.
1215
- 2. Otherwise, create a new entry.
1216
- * **archive**:
1217
- 1. If model endpoints with the same name exist, preserve them.
1218
- 2. Create a new model endpoint with the same name and set it to `latest`.
1224
+
1225
+ * **overwrite**:
1226
+
1227
+ 1. If model endpoints with the same name exist, delete the `latest` one.
1228
+ 2. Create a new model endpoint entry and set it as `latest`.
1229
+
1230
+ * **inplace** (default):
1231
+
1232
+ 1. If model endpoints with the same name exist, update the `latest` entry.
1233
+ 2. Otherwise, create a new entry.
1234
+
1235
+ * **archive**:
1236
+
1237
+ 1. If model endpoints with the same name exist, preserve them.
1238
+ 2. Create a new model endpoint with the same name and set it to `latest`.
1239
+
1219
1240
  :param class_args: class init arguments
1220
1241
  """
1221
1242