mlrun 1.8.0rc27__py3-none-any.whl → 1.8.0rc29__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.
- mlrun/artifacts/document.py +9 -6
- mlrun/common/schemas/serving.py +22 -0
- mlrun/config.py +9 -0
- mlrun/datastore/base.py +0 -7
- mlrun/datastore/s3.py +9 -2
- mlrun/db/base.py +0 -1
- mlrun/db/httpdb.py +11 -14
- mlrun/db/nopdb.py +0 -1
- mlrun/execution.py +15 -4
- mlrun/model_monitoring/applications/_application_steps.py +1 -0
- mlrun/model_monitoring/applications/base.py +132 -21
- mlrun/model_monitoring/applications/context.py +2 -3
- mlrun/model_monitoring/controller.py +117 -57
- mlrun/model_monitoring/db/_schedules.py +8 -0
- mlrun/model_monitoring/db/tsdb/__init__.py +12 -5
- mlrun/model_monitoring/stream_processing.py +3 -2
- mlrun/projects/project.py +38 -7
- mlrun/runtimes/base.py +1 -1
- mlrun/runtimes/generators.py +1 -1
- mlrun/runtimes/nuclio/function.py +39 -0
- mlrun/runtimes/nuclio/serving.py +3 -0
- mlrun/runtimes/pod.py +1 -3
- mlrun/serving/routers.py +62 -17
- mlrun/serving/server.py +11 -0
- mlrun/serving/states.py +0 -4
- mlrun/serving/v2_serving.py +45 -10
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc27.dist-info → mlrun-1.8.0rc29.dist-info}/METADATA +4 -2
- {mlrun-1.8.0rc27.dist-info → mlrun-1.8.0rc29.dist-info}/RECORD +33 -32
- {mlrun-1.8.0rc27.dist-info → mlrun-1.8.0rc29.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc27.dist-info → mlrun-1.8.0rc29.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc27.dist-info → mlrun-1.8.0rc29.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc27.dist-info → mlrun-1.8.0rc29.dist-info}/top_level.txt +0 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import concurrent.futures
|
|
15
16
|
import datetime
|
|
16
17
|
import json
|
|
17
18
|
import os
|
|
@@ -138,7 +139,9 @@ class _BatchWindow:
|
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
class _BatchWindowGenerator(AbstractContextManager):
|
|
141
|
-
def __init__(
|
|
142
|
+
def __init__(
|
|
143
|
+
self, project: str, endpoint_id: str, window_length: Optional[int] = None
|
|
144
|
+
) -> None:
|
|
142
145
|
"""
|
|
143
146
|
Initialize a batch window generator object that generates batch window objects
|
|
144
147
|
for the monitoring functions.
|
|
@@ -165,6 +168,12 @@ class _BatchWindowGenerator(AbstractContextManager):
|
|
|
165
168
|
exc_type=exc_type, exc_value=exc_value, traceback=traceback
|
|
166
169
|
)
|
|
167
170
|
|
|
171
|
+
def get_application_list(self) -> set[str]:
|
|
172
|
+
return self._schedules_file.get_application_list()
|
|
173
|
+
|
|
174
|
+
def get_min_last_analyzed(self) -> Optional[int]:
|
|
175
|
+
return self._schedules_file.get_min_timestamp()
|
|
176
|
+
|
|
168
177
|
@classmethod
|
|
169
178
|
def _get_last_updated_time(
|
|
170
179
|
cls, last_request: datetime.datetime, not_batch_endpoint: bool
|
|
@@ -234,8 +243,7 @@ class MonitoringApplicationController:
|
|
|
234
243
|
def __init__(self) -> None:
|
|
235
244
|
"""Initialize Monitoring Application Controller"""
|
|
236
245
|
self.project = cast(str, mlrun.mlconf.default_project)
|
|
237
|
-
self.project_obj = mlrun.
|
|
238
|
-
|
|
246
|
+
self.project_obj = mlrun.get_run_db().get_project(name=self.project)
|
|
239
247
|
logger.debug(f"Initializing {self.__class__.__name__}", project=self.project)
|
|
240
248
|
|
|
241
249
|
self._window_length = _get_window_length()
|
|
@@ -255,8 +263,10 @@ class MonitoringApplicationController:
|
|
|
255
263
|
return access_key
|
|
256
264
|
|
|
257
265
|
@staticmethod
|
|
258
|
-
def _should_monitor_endpoint(
|
|
259
|
-
|
|
266
|
+
def _should_monitor_endpoint(
|
|
267
|
+
endpoint: mlrun.common.schemas.ModelEndpoint, application_names: set
|
|
268
|
+
) -> bool:
|
|
269
|
+
if (
|
|
260
270
|
# Is the model endpoint monitored?
|
|
261
271
|
endpoint.status.monitoring_mode == mm_constants.ModelMonitoringMode.enabled
|
|
262
272
|
# Was the model endpoint called? I.e., are the first and last requests nonempty?
|
|
@@ -265,7 +275,40 @@ class MonitoringApplicationController:
|
|
|
265
275
|
# Is the model endpoint not a router endpoint? Router endpoint has no feature stats
|
|
266
276
|
and endpoint.metadata.endpoint_type.value
|
|
267
277
|
!= mm_constants.EndpointType.ROUTER.value
|
|
268
|
-
)
|
|
278
|
+
):
|
|
279
|
+
with _BatchWindowGenerator(
|
|
280
|
+
project=endpoint.metadata.project,
|
|
281
|
+
endpoint_id=endpoint.metadata.uid,
|
|
282
|
+
) as batch_window_generator:
|
|
283
|
+
if application_names != batch_window_generator.get_application_list():
|
|
284
|
+
return True
|
|
285
|
+
elif (
|
|
286
|
+
not batch_window_generator.get_min_last_analyzed()
|
|
287
|
+
or batch_window_generator.get_min_last_analyzed()
|
|
288
|
+
<= int(endpoint.status.last_request.timestamp())
|
|
289
|
+
):
|
|
290
|
+
return True
|
|
291
|
+
else:
|
|
292
|
+
logger.info(
|
|
293
|
+
"All the possible intervals were already analyzed, didn't push regular event",
|
|
294
|
+
endpoint_id=endpoint.metadata.uid,
|
|
295
|
+
last_analyzed=datetime.datetime.fromtimestamp(
|
|
296
|
+
batch_window_generator.get_min_last_analyzed(),
|
|
297
|
+
tz=datetime.timezone.utc,
|
|
298
|
+
),
|
|
299
|
+
last_request=endpoint.status.last_request,
|
|
300
|
+
)
|
|
301
|
+
else:
|
|
302
|
+
logger.info(
|
|
303
|
+
"Should not monitor model endpoint, didn't push regular event",
|
|
304
|
+
endpoint_id=endpoint.metadata.uid,
|
|
305
|
+
endpoint_name=endpoint.metadata.name,
|
|
306
|
+
last_request=endpoint.status.last_request,
|
|
307
|
+
first_request=endpoint.status.first_request,
|
|
308
|
+
endpoint_type=endpoint.metadata.endpoint_type,
|
|
309
|
+
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
310
|
+
)
|
|
311
|
+
return False
|
|
269
312
|
|
|
270
313
|
def run(self, event: nuclio_sdk.Event) -> None:
|
|
271
314
|
"""
|
|
@@ -314,7 +357,7 @@ class MonitoringApplicationController:
|
|
|
314
357
|
)
|
|
315
358
|
m_fs = fstore.get_feature_set(event[ControllerEvent.FEATURE_SET_URI])
|
|
316
359
|
logger.info(
|
|
317
|
-
"Starting analyzing for
|
|
360
|
+
"Starting analyzing for", timestamp=event[ControllerEvent.TIMESTAMP]
|
|
318
361
|
)
|
|
319
362
|
last_stream_timestamp = datetime.datetime.fromisoformat(
|
|
320
363
|
event[ControllerEvent.TIMESTAMP]
|
|
@@ -370,7 +413,7 @@ class MonitoringApplicationController:
|
|
|
370
413
|
current_time = mlrun.utils.datetime_now()
|
|
371
414
|
if (
|
|
372
415
|
current_time.timestamp()
|
|
373
|
-
- batch_window_generator.
|
|
416
|
+
- batch_window_generator.get_min_last_analyzed()
|
|
374
417
|
>= datetime.timedelta(minutes=base_period).total_seconds()
|
|
375
418
|
and event[ControllerEvent.KIND] != ControllerEventKind.NOP_EVENT
|
|
376
419
|
):
|
|
@@ -399,6 +442,9 @@ class MonitoringApplicationController:
|
|
|
399
442
|
event=event,
|
|
400
443
|
endpoint_id=endpoint_id,
|
|
401
444
|
)
|
|
445
|
+
logger.info(
|
|
446
|
+
"Finish analyze for", timestamp=event[ControllerEvent.TIMESTAMP]
|
|
447
|
+
)
|
|
402
448
|
|
|
403
449
|
except Exception:
|
|
404
450
|
logger.exception(
|
|
@@ -455,17 +501,14 @@ class MonitoringApplicationController:
|
|
|
455
501
|
[data]
|
|
456
502
|
)
|
|
457
503
|
|
|
458
|
-
def push_regular_event_to_controller_stream(self
|
|
504
|
+
def push_regular_event_to_controller_stream(self) -> None:
|
|
459
505
|
"""
|
|
460
506
|
pushes a regular event to the controller stream.
|
|
461
507
|
:param event: the nuclio trigger event
|
|
462
508
|
"""
|
|
463
509
|
logger.info("Starting monitoring controller chief")
|
|
464
510
|
applications_names = []
|
|
465
|
-
|
|
466
|
-
endpoints = db.list_model_endpoints(
|
|
467
|
-
project=self.project, tsdb_metrics=True
|
|
468
|
-
).endpoints
|
|
511
|
+
endpoints = self.project_obj.list_model_endpoints(tsdb_metrics=True).endpoints
|
|
469
512
|
if not endpoints:
|
|
470
513
|
logger.info("No model endpoints found", project=self.project)
|
|
471
514
|
return
|
|
@@ -505,48 +548,59 @@ class MonitoringApplicationController:
|
|
|
505
548
|
// 60
|
|
506
549
|
),
|
|
507
550
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
),
|
|
517
|
-
|
|
518
|
-
sep=" ", timespec="microseconds"
|
|
519
|
-
),
|
|
520
|
-
endpoint_type=endpoint.metadata.endpoint_type,
|
|
521
|
-
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
522
|
-
endpoint_policy=json.dumps(policy),
|
|
523
|
-
)
|
|
524
|
-
self.push_to_controller_stream(
|
|
525
|
-
kind=mm_constants.ControllerEventKind.REGULAR_EVENT,
|
|
526
|
-
project=self.project,
|
|
527
|
-
endpoint_id=endpoint.metadata.uid,
|
|
528
|
-
endpoint_name=endpoint.metadata.name,
|
|
529
|
-
stream_access_key=self.v3io_access_key,
|
|
530
|
-
timestamp=endpoint.status.last_request.isoformat(
|
|
531
|
-
sep=" ", timespec="microseconds"
|
|
532
|
-
),
|
|
533
|
-
first_request=endpoint.status.first_request.isoformat(
|
|
534
|
-
sep=" ", timespec="microseconds"
|
|
535
|
-
),
|
|
536
|
-
endpoint_type=endpoint.metadata.endpoint_type,
|
|
537
|
-
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
538
|
-
endpoint_policy=policy,
|
|
539
|
-
)
|
|
540
|
-
else:
|
|
541
|
-
logger.info(
|
|
542
|
-
"Should not monitor model endpoint, didn't push regular event",
|
|
543
|
-
endpoint_id=endpoint.metadata.uid,
|
|
544
|
-
endpoint_name=endpoint.metadata.name,
|
|
545
|
-
timestamp=endpoint.status.last_request,
|
|
546
|
-
first_request=endpoint.status.first_request,
|
|
547
|
-
endpoint_type=endpoint.metadata.endpoint_type,
|
|
548
|
-
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
551
|
+
with concurrent.futures.ThreadPoolExecutor(
|
|
552
|
+
max_workers=min(len(endpoints), 10)
|
|
553
|
+
) as pool:
|
|
554
|
+
for endpoint in endpoints:
|
|
555
|
+
pool.submit(
|
|
556
|
+
MonitoringApplicationController.endpoint_to_regular_event,
|
|
557
|
+
endpoint,
|
|
558
|
+
policy,
|
|
559
|
+
set(applications_names),
|
|
560
|
+
self.v3io_access_key,
|
|
549
561
|
)
|
|
562
|
+
logger.info("Finishing monitoring controller chief")
|
|
563
|
+
|
|
564
|
+
@staticmethod
|
|
565
|
+
def endpoint_to_regular_event(
|
|
566
|
+
endpoint: mlrun.common.schemas.ModelEndpoint,
|
|
567
|
+
policy: dict,
|
|
568
|
+
applications_names: set,
|
|
569
|
+
v3io_access_key: str,
|
|
570
|
+
) -> None:
|
|
571
|
+
if MonitoringApplicationController._should_monitor_endpoint(
|
|
572
|
+
endpoint, set(applications_names)
|
|
573
|
+
):
|
|
574
|
+
logger.info(
|
|
575
|
+
"Regular event is being pushed to controller stream for model endpoint",
|
|
576
|
+
endpoint_id=endpoint.metadata.uid,
|
|
577
|
+
endpoint_name=endpoint.metadata.name,
|
|
578
|
+
timestamp=endpoint.status.last_request.isoformat(
|
|
579
|
+
sep=" ", timespec="microseconds"
|
|
580
|
+
),
|
|
581
|
+
first_request=endpoint.status.first_request.isoformat(
|
|
582
|
+
sep=" ", timespec="microseconds"
|
|
583
|
+
),
|
|
584
|
+
endpoint_type=endpoint.metadata.endpoint_type,
|
|
585
|
+
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
586
|
+
endpoint_policy=json.dumps(policy),
|
|
587
|
+
)
|
|
588
|
+
MonitoringApplicationController.push_to_controller_stream(
|
|
589
|
+
kind=mm_constants.ControllerEventKind.REGULAR_EVENT,
|
|
590
|
+
project=endpoint.metadata.project,
|
|
591
|
+
endpoint_id=endpoint.metadata.uid,
|
|
592
|
+
endpoint_name=endpoint.metadata.name,
|
|
593
|
+
stream_access_key=v3io_access_key,
|
|
594
|
+
timestamp=endpoint.status.last_request.isoformat(
|
|
595
|
+
sep=" ", timespec="microseconds"
|
|
596
|
+
),
|
|
597
|
+
first_request=endpoint.status.first_request.isoformat(
|
|
598
|
+
sep=" ", timespec="microseconds"
|
|
599
|
+
),
|
|
600
|
+
endpoint_type=endpoint.metadata.endpoint_type.value,
|
|
601
|
+
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
602
|
+
endpoint_policy=policy,
|
|
603
|
+
)
|
|
550
604
|
|
|
551
605
|
@staticmethod
|
|
552
606
|
def push_to_controller_stream(
|
|
@@ -557,7 +611,7 @@ class MonitoringApplicationController:
|
|
|
557
611
|
stream_access_key: str,
|
|
558
612
|
timestamp: str,
|
|
559
613
|
first_request: str,
|
|
560
|
-
endpoint_type:
|
|
614
|
+
endpoint_type: int,
|
|
561
615
|
feature_set_uri: str,
|
|
562
616
|
endpoint_policy: dict[str, Any],
|
|
563
617
|
) -> None:
|
|
@@ -633,7 +687,13 @@ def handler(context: nuclio_sdk.Context, event: nuclio_sdk.Event) -> None:
|
|
|
633
687
|
|
|
634
688
|
if event.trigger.kind == "http":
|
|
635
689
|
# Runs controller chief:
|
|
636
|
-
|
|
690
|
+
context.user_data.monitor_app_controller.push_regular_event_to_controller_stream()
|
|
637
691
|
else:
|
|
638
692
|
# Runs controller worker:
|
|
639
|
-
|
|
693
|
+
context.user_data.monitor_app_controller.run(event)
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def init_context(context):
|
|
697
|
+
monitor_app_controller = MonitoringApplicationController()
|
|
698
|
+
setattr(context.user_data, "monitor_app_controller", monitor_app_controller)
|
|
699
|
+
context.logger.info("Monitoring application controller initialized")
|
|
@@ -140,6 +140,14 @@ class ModelMonitoringSchedulesFile(AbstractContextManager):
|
|
|
140
140
|
self._check_open_schedules()
|
|
141
141
|
self._schedules[application] = timestamp
|
|
142
142
|
|
|
143
|
+
def get_application_list(self) -> set[str]:
|
|
144
|
+
self._check_open_schedules()
|
|
145
|
+
return set(self._schedules.keys())
|
|
146
|
+
|
|
147
|
+
def get_min_timestamp(self) -> Optional[int]:
|
|
148
|
+
self._check_open_schedules()
|
|
149
|
+
return min(self._schedules.values(), default=None)
|
|
150
|
+
|
|
143
151
|
|
|
144
152
|
def delete_model_monitoring_schedules_folder(project: str) -> None:
|
|
145
153
|
"""Delete the model monitoring schedules folder of the project"""
|
|
@@ -75,10 +75,11 @@ def get_tsdb_connector(
|
|
|
75
75
|
:param secret_provider: An optional secret provider to get the connection string secret.
|
|
76
76
|
:param profile: An optional profile to initialize the TSDB connector from.
|
|
77
77
|
|
|
78
|
-
:return:
|
|
78
|
+
:return: ``TSDBConnector`` object. The main goal of this object is to handle different operations on the
|
|
79
79
|
TSDB connector such as updating drift metrics or write application record result.
|
|
80
|
-
:raise:
|
|
81
|
-
|
|
80
|
+
:raise: ``MLRunNotFoundError`` if the user didn't set the TSDB datastore profile and didn't provide it through
|
|
81
|
+
the ``profile`` parameter.
|
|
82
|
+
:raise: ``MLRunInvalidMMStoreTypeError`` if the TSDB datastore profile is of an invalid type.
|
|
82
83
|
"""
|
|
83
84
|
profile = profile or mlrun.model_monitoring.helpers._get_tsdb_profile(
|
|
84
85
|
project=project, secret_provider=secret_provider
|
|
@@ -93,9 +94,15 @@ def get_tsdb_connector(
|
|
|
93
94
|
tsdb_connector_type = mlrun.common.schemas.model_monitoring.TSDBTarget.TDEngine
|
|
94
95
|
kwargs["connection_string"] = profile.dsn()
|
|
95
96
|
else:
|
|
97
|
+
extra_message = (
|
|
98
|
+
""
|
|
99
|
+
if profile
|
|
100
|
+
else " by using `project.set_model_monitoring_credentials` API"
|
|
101
|
+
)
|
|
96
102
|
raise mlrun.errors.MLRunInvalidMMStoreTypeError(
|
|
97
|
-
"You must provide a valid
|
|
98
|
-
"
|
|
103
|
+
"You must provide a valid TSDB datastore profile"
|
|
104
|
+
f"{extra_message}. "
|
|
105
|
+
f"Found an unexpected profile of class: {type(profile)}"
|
|
99
106
|
)
|
|
100
107
|
|
|
101
108
|
# Get connector type value from ObjectTSDBFactory enum class
|
|
@@ -378,7 +378,9 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
378
378
|
def do(self, full_event):
|
|
379
379
|
event = full_event.body
|
|
380
380
|
if event.get(ControllerEvent.KIND, "") == ControllerEventKind.NOP_EVENT:
|
|
381
|
-
logger.
|
|
381
|
+
logger.debug(
|
|
382
|
+
"Skipped nop event inside of ProcessEndpointEvent", event=event
|
|
383
|
+
)
|
|
382
384
|
return storey.Event(body=[event])
|
|
383
385
|
# Getting model version and function uri from event
|
|
384
386
|
# and use them for retrieving the endpoint_id
|
|
@@ -637,7 +639,6 @@ class MapFeatureNames(mlrun.feature_store.steps.MapClass):
|
|
|
637
639
|
|
|
638
640
|
def do(self, event: dict):
|
|
639
641
|
if event.get(ControllerEvent.KIND, "") == ControllerEventKind.NOP_EVENT:
|
|
640
|
-
logger.info("Skipped nop event inside of MapFeatureNames", event=event)
|
|
641
642
|
return event
|
|
642
643
|
endpoint_id = event[EventFieldType.ENDPOINT_ID]
|
|
643
644
|
|
mlrun/projects/project.py
CHANGED
|
@@ -31,6 +31,7 @@ from os import environ, makedirs, path
|
|
|
31
31
|
from typing import Callable, Optional, Union, cast
|
|
32
32
|
from urllib.parse import urlparse
|
|
33
33
|
|
|
34
|
+
import deprecated
|
|
34
35
|
import dotenv
|
|
35
36
|
import git
|
|
36
37
|
import git.exc
|
|
@@ -1948,7 +1949,8 @@ class MlrunProject(ModelObj):
|
|
|
1948
1949
|
kwargs={"extract_images": True}
|
|
1949
1950
|
)
|
|
1950
1951
|
:param upload: Whether to upload the artifact
|
|
1951
|
-
:param labels:
|
|
1952
|
+
:param labels: Key-value labels. A 'source' label is automatically added using either
|
|
1953
|
+
local_path or target_path to facilitate easier document searching.
|
|
1952
1954
|
:param target_path: Target file path
|
|
1953
1955
|
:param kwargs: Additional keyword arguments
|
|
1954
1956
|
:return: DocumentArtifact object
|
|
@@ -1978,13 +1980,24 @@ class MlrunProject(ModelObj):
|
|
|
1978
1980
|
"The document loader is configured to not support downloads but the upload flag is set to True."
|
|
1979
1981
|
"Either set loader.download_object=True or set upload=False"
|
|
1980
1982
|
)
|
|
1983
|
+
original_source = local_path or target_path
|
|
1981
1984
|
doc_artifact = DocumentArtifact(
|
|
1982
1985
|
key=key,
|
|
1983
|
-
original_source=
|
|
1986
|
+
original_source=original_source,
|
|
1984
1987
|
document_loader_spec=document_loader_spec,
|
|
1985
1988
|
collections=kwargs.pop("collections", None),
|
|
1986
1989
|
**kwargs,
|
|
1987
1990
|
)
|
|
1991
|
+
|
|
1992
|
+
# limit label to a max of 255 characters (for db reasons)
|
|
1993
|
+
max_length = 255
|
|
1994
|
+
labels = labels or {}
|
|
1995
|
+
labels["source"] = (
|
|
1996
|
+
original_source[: max_length - 3] + "..."
|
|
1997
|
+
if len(original_source) > max_length
|
|
1998
|
+
else original_source
|
|
1999
|
+
)
|
|
2000
|
+
|
|
1988
2001
|
return self.log_artifact(
|
|
1989
2002
|
item=doc_artifact,
|
|
1990
2003
|
tag=tag,
|
|
@@ -2406,7 +2419,6 @@ class MlrunProject(ModelObj):
|
|
|
2406
2419
|
*,
|
|
2407
2420
|
deploy_histogram_data_drift_app: bool = True,
|
|
2408
2421
|
wait_for_deployment: bool = False,
|
|
2409
|
-
rebuild_images: bool = False,
|
|
2410
2422
|
fetch_credentials_from_sys_config: bool = False,
|
|
2411
2423
|
) -> None:
|
|
2412
2424
|
"""
|
|
@@ -2428,7 +2440,6 @@ class MlrunProject(ModelObj):
|
|
|
2428
2440
|
:param wait_for_deployment: If true, return only after the deployment is done on the backend.
|
|
2429
2441
|
Otherwise, deploy the model monitoring infrastructure on the
|
|
2430
2442
|
background, including the histogram data drift app if selected.
|
|
2431
|
-
:param rebuild_images: If true, force rebuild of model monitoring infrastructure images.
|
|
2432
2443
|
:param fetch_credentials_from_sys_config: If true, fetch the credentials from the system configuration.
|
|
2433
2444
|
"""
|
|
2434
2445
|
if default_controller_image != "mlrun/mlrun":
|
|
@@ -2451,7 +2462,6 @@ class MlrunProject(ModelObj):
|
|
|
2451
2462
|
image=image,
|
|
2452
2463
|
base_period=base_period,
|
|
2453
2464
|
deploy_histogram_data_drift_app=deploy_histogram_data_drift_app,
|
|
2454
|
-
rebuild_images=rebuild_images,
|
|
2455
2465
|
fetch_credentials_from_sys_config=fetch_credentials_from_sys_config,
|
|
2456
2466
|
)
|
|
2457
2467
|
|
|
@@ -2839,6 +2849,13 @@ class MlrunProject(ModelObj):
|
|
|
2839
2849
|
|
|
2840
2850
|
self.spec.set_function(name, function_object, func)
|
|
2841
2851
|
|
|
2852
|
+
# TODO: Remove this in 1.10.0
|
|
2853
|
+
@deprecated.deprecated(
|
|
2854
|
+
version="1.8.0",
|
|
2855
|
+
reason="'remove_function' is deprecated and will be removed in 1.10.0. "
|
|
2856
|
+
"Please use `delete_function` instead.",
|
|
2857
|
+
category=FutureWarning,
|
|
2858
|
+
)
|
|
2842
2859
|
def remove_function(self, name):
|
|
2843
2860
|
"""remove the specified function from the project
|
|
2844
2861
|
|
|
@@ -2846,6 +2863,18 @@ class MlrunProject(ModelObj):
|
|
|
2846
2863
|
"""
|
|
2847
2864
|
self.spec.remove_function(name)
|
|
2848
2865
|
|
|
2866
|
+
def delete_function(self, name, delete_from_db=False):
|
|
2867
|
+
"""deletes the specified function from the project
|
|
2868
|
+
|
|
2869
|
+
:param name: name of the function (under the project)
|
|
2870
|
+
:param delete_from_db: default is False. If False, the function is removed
|
|
2871
|
+
only from the project's cache and spec.
|
|
2872
|
+
If True, the function is also removed from the database.
|
|
2873
|
+
"""
|
|
2874
|
+
if delete_from_db:
|
|
2875
|
+
mlrun.db.get_run_db().delete_function(name=name, project=self.metadata.name)
|
|
2876
|
+
self.spec.remove_function(name)
|
|
2877
|
+
|
|
2849
2878
|
def remove_model_monitoring_function(self, name: Union[str, list[str]]):
|
|
2850
2879
|
"""delete the specified model-monitoring-app function/s
|
|
2851
2880
|
|
|
@@ -3762,8 +3791,8 @@ class MlrunProject(ModelObj):
|
|
|
3762
3791
|
"Please keep in mind that if you already had model monitoring functions "
|
|
3763
3792
|
"/ model monitoring infra / tracked model server "
|
|
3764
3793
|
"deployed on your project, you will need to redeploy them. "
|
|
3765
|
-
"For redeploying the model monitoring infra,
|
|
3766
|
-
"and
|
|
3794
|
+
"For redeploying the model monitoring infra, first disable it using "
|
|
3795
|
+
"`project.disable_model_monitoring()` and then enable it using `project.enable_model_monitoring()`."
|
|
3767
3796
|
)
|
|
3768
3797
|
|
|
3769
3798
|
def list_model_endpoints(
|
|
@@ -3779,6 +3808,7 @@ class MlrunProject(ModelObj):
|
|
|
3779
3808
|
top_level: bool = False,
|
|
3780
3809
|
uids: Optional[list[str]] = None,
|
|
3781
3810
|
latest_only: bool = False,
|
|
3811
|
+
tsdb_metrics: bool = True,
|
|
3782
3812
|
) -> mlrun.common.schemas.ModelEndpointList:
|
|
3783
3813
|
"""
|
|
3784
3814
|
Returns a list of `ModelEndpoint` objects. Each `ModelEndpoint` object represents the current state of a
|
|
@@ -3829,6 +3859,7 @@ class MlrunProject(ModelObj):
|
|
|
3829
3859
|
top_level=top_level,
|
|
3830
3860
|
uids=uids,
|
|
3831
3861
|
latest_only=latest_only,
|
|
3862
|
+
tsdb_metrics=tsdb_metrics,
|
|
3832
3863
|
)
|
|
3833
3864
|
|
|
3834
3865
|
def run_function(
|
mlrun/runtimes/base.py
CHANGED
mlrun/runtimes/generators.py
CHANGED
|
@@ -599,6 +599,18 @@ class RemoteRuntime(KubeResource):
|
|
|
599
599
|
# when a function is deployed, we wait for it to be ready by default
|
|
600
600
|
# this also means that the function object will be updated with the function status
|
|
601
601
|
self._wait_for_function_deployment(db, verbose=verbose)
|
|
602
|
+
# check if there are any background tasks related to creating model endpoints
|
|
603
|
+
model_endpoints_creation_background_tasks = (
|
|
604
|
+
mlrun.common.schemas.BackgroundTaskList(
|
|
605
|
+
**data.pop("background_tasks", {"background_tasks": []})
|
|
606
|
+
).background_tasks
|
|
607
|
+
)
|
|
608
|
+
if model_endpoints_creation_background_tasks:
|
|
609
|
+
self._check_model_endpoint_task_state(
|
|
610
|
+
db=db,
|
|
611
|
+
background_task=model_endpoints_creation_background_tasks[0],
|
|
612
|
+
wait_for_completion=False,
|
|
613
|
+
)
|
|
602
614
|
|
|
603
615
|
return self._enrich_command_from_status()
|
|
604
616
|
|
|
@@ -1285,6 +1297,33 @@ class RemoteRuntime(KubeResource):
|
|
|
1285
1297
|
return mlrun.model.Credentials.generate_access_key
|
|
1286
1298
|
return None
|
|
1287
1299
|
|
|
1300
|
+
def _check_model_endpoint_task_state(
|
|
1301
|
+
self,
|
|
1302
|
+
db: mlrun.db.RunDBInterface,
|
|
1303
|
+
background_task: mlrun.common.schemas.BackgroundTask,
|
|
1304
|
+
wait_for_completion: bool,
|
|
1305
|
+
):
|
|
1306
|
+
if wait_for_completion:
|
|
1307
|
+
background_task = db._wait_for_background_task_to_reach_terminal_state(
|
|
1308
|
+
name=background_task.metadata.name, project=self.metadata.project
|
|
1309
|
+
)
|
|
1310
|
+
else:
|
|
1311
|
+
background_task = db.get_project_background_task(
|
|
1312
|
+
project=self.metadata.project, name=background_task.metadata.name
|
|
1313
|
+
)
|
|
1314
|
+
if (
|
|
1315
|
+
background_task.status.state
|
|
1316
|
+
in mlrun.common.schemas.BackgroundTaskState.terminal_states()
|
|
1317
|
+
):
|
|
1318
|
+
logger.info(
|
|
1319
|
+
f"Model endpoint creation task completed with state {background_task.status.state}"
|
|
1320
|
+
)
|
|
1321
|
+
else:
|
|
1322
|
+
logger.warning(
|
|
1323
|
+
f"Model endpoint creation task is still running with state {background_task.status.state}"
|
|
1324
|
+
f"You can use the serving function, but it won't be monitored for the next few minutes"
|
|
1325
|
+
)
|
|
1326
|
+
|
|
1288
1327
|
|
|
1289
1328
|
def parse_logs(logs):
|
|
1290
1329
|
logs = json.loads(logs)
|
mlrun/runtimes/nuclio/serving.py
CHANGED
|
@@ -152,6 +152,7 @@ class ServingSpec(NuclioSpec):
|
|
|
152
152
|
clone_target_dir=None,
|
|
153
153
|
state_thresholds=None,
|
|
154
154
|
disable_default_http_trigger=None,
|
|
155
|
+
model_endpoint_creation_task_name=None,
|
|
155
156
|
):
|
|
156
157
|
super().__init__(
|
|
157
158
|
command=command,
|
|
@@ -209,6 +210,7 @@ class ServingSpec(NuclioSpec):
|
|
|
209
210
|
self.tracking_policy = tracking_policy
|
|
210
211
|
self.secret_sources = secret_sources or []
|
|
211
212
|
self.default_content_type = default_content_type
|
|
213
|
+
self.model_endpoint_creation_task_name = model_endpoint_creation_task_name
|
|
212
214
|
|
|
213
215
|
@property
|
|
214
216
|
def graph(self) -> Union[RouterStep, RootFlowStep]:
|
|
@@ -696,6 +698,7 @@ class ServingRuntime(RemoteRuntime):
|
|
|
696
698
|
"track_models": self.spec.track_models,
|
|
697
699
|
"tracking_policy": None,
|
|
698
700
|
"default_content_type": self.spec.default_content_type,
|
|
701
|
+
"model_endpoint_creation_task_name": self.spec.model_endpoint_creation_task_name,
|
|
699
702
|
}
|
|
700
703
|
|
|
701
704
|
if self.spec.secret_sources:
|
mlrun/runtimes/pod.py
CHANGED
|
@@ -214,9 +214,7 @@ class KubeResourceSpec(FunctionSpec):
|
|
|
214
214
|
# default service account is set in mlrun.utils.process_function_service_account
|
|
215
215
|
# due to project specific defaults
|
|
216
216
|
self.service_account = service_account
|
|
217
|
-
self.image_pull_secret =
|
|
218
|
-
image_pull_secret or mlrun.mlconf.function.spec.image_pull_secret.default
|
|
219
|
-
)
|
|
217
|
+
self.image_pull_secret = image_pull_secret
|
|
220
218
|
self.node_name = node_name
|
|
221
219
|
self.node_selector = node_selector or {}
|
|
222
220
|
self._affinity = affinity
|