mlrun 1.7.0rc32__py3-none-any.whl → 1.7.0rc34__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/common/schemas/__init__.py +1 -0
- mlrun/common/schemas/common.py +3 -0
- mlrun/common/schemas/function.py +7 -0
- mlrun/common/schemas/project.py +35 -3
- mlrun/config.py +9 -1
- mlrun/datastore/base.py +5 -1
- mlrun/db/base.py +8 -3
- mlrun/db/httpdb.py +10 -8
- mlrun/db/nopdb.py +3 -1
- mlrun/execution.py +1 -3
- mlrun/model.py +142 -22
- mlrun/model_monitoring/applications/context.py +13 -15
- mlrun/model_monitoring/controller.py +1 -1
- mlrun/model_monitoring/db/stores/base/store.py +2 -0
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +9 -23
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +5 -20
- mlrun/model_monitoring/stream_processing.py +6 -0
- mlrun/projects/project.py +8 -2
- mlrun/run.py +22 -9
- mlrun/runtimes/nuclio/api_gateway.py +37 -7
- mlrun/runtimes/nuclio/application/application.py +50 -9
- mlrun/runtimes/nuclio/function.py +3 -0
- mlrun/runtimes/nuclio/serving.py +5 -5
- mlrun/serving/server.py +12 -7
- mlrun/serving/states.py +13 -1
- mlrun/utils/db.py +3 -0
- mlrun/utils/helpers.py +3 -5
- mlrun/utils/notifications/notification/webhook.py +8 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/METADATA +3 -3
- {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/RECORD +35 -35
- {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/WHEEL +1 -1
- {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py
CHANGED
|
@@ -725,7 +725,7 @@ def _project_instance_from_struct(struct, name, allow_cross_project):
|
|
|
725
725
|
# TODO: Remove this warning in version 1.9.0 and also fix cli to support allow_cross_project
|
|
726
726
|
warnings.warn(
|
|
727
727
|
f"Project {name=} is different than specified on the context's project yaml. "
|
|
728
|
-
"This behavior is deprecated and will not be supported
|
|
728
|
+
"This behavior is deprecated and will not be supported from version 1.9.0."
|
|
729
729
|
)
|
|
730
730
|
logger.warn(error_message)
|
|
731
731
|
elif allow_cross_project:
|
|
@@ -3781,7 +3781,7 @@ class MlrunProject(ModelObj):
|
|
|
3781
3781
|
|
|
3782
3782
|
|
|
3783
3783
|
:param name: Return only functions with a specific name.
|
|
3784
|
-
:param tag: Return function versions with specific tags.
|
|
3784
|
+
:param tag: Return function versions with specific tags. To return only tagged functions, set tag to ``"*"``.
|
|
3785
3785
|
:param labels: Return functions that have specific labels assigned to them.
|
|
3786
3786
|
:returns: List of function objects.
|
|
3787
3787
|
"""
|
|
@@ -4063,6 +4063,12 @@ class MlrunProject(ModelObj):
|
|
|
4063
4063
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
4064
4064
|
if alert_name is None:
|
|
4065
4065
|
alert_name = alert_data.name
|
|
4066
|
+
if alert_data.project is not None and alert_data.project != self.metadata.name:
|
|
4067
|
+
logger.warn(
|
|
4068
|
+
"Project in alert does not match project in operation",
|
|
4069
|
+
project=alert_data.project,
|
|
4070
|
+
)
|
|
4071
|
+
alert_data.project = self.metadata.name
|
|
4066
4072
|
return db.store_alert_config(alert_name, alert_data, project=self.metadata.name)
|
|
4067
4073
|
|
|
4068
4074
|
def get_alert_config(self, alert_name: str) -> AlertConfig:
|
mlrun/run.py
CHANGED
|
@@ -21,6 +21,7 @@ import tempfile
|
|
|
21
21
|
import time
|
|
22
22
|
import typing
|
|
23
23
|
import uuid
|
|
24
|
+
import warnings
|
|
24
25
|
from base64 import b64decode
|
|
25
26
|
from copy import deepcopy
|
|
26
27
|
from os import environ, makedirs, path
|
|
@@ -196,18 +197,19 @@ def load_func_code(command="", workdir=None, secrets=None, name="name"):
|
|
|
196
197
|
def get_or_create_ctx(
|
|
197
198
|
name: str,
|
|
198
199
|
event=None,
|
|
199
|
-
spec=None,
|
|
200
|
+
spec: Optional[dict] = None,
|
|
200
201
|
with_env: bool = True,
|
|
201
202
|
rundb: str = "",
|
|
202
203
|
project: str = "",
|
|
203
|
-
upload_artifacts=False,
|
|
204
|
+
upload_artifacts: bool = False,
|
|
204
205
|
labels: Optional[dict] = None,
|
|
205
206
|
) -> MLClientCtx:
|
|
206
|
-
"""
|
|
207
|
+
"""
|
|
208
|
+
Called from within the user program to obtain a run context.
|
|
207
209
|
|
|
208
|
-
|
|
210
|
+
The run context is an interface for receiving parameters, data and logging
|
|
209
211
|
run results, the run context is read from the event, spec, or environment
|
|
210
|
-
(in that order), user can also work without a context (local defaults mode)
|
|
212
|
+
(in that order), user can also work without a context (local defaults mode).
|
|
211
213
|
|
|
212
214
|
all results are automatically stored in the "rundb" or artifact store,
|
|
213
215
|
the path to the rundb can be specified in the call or obtained from env.
|
|
@@ -220,7 +222,7 @@ def get_or_create_ctx(
|
|
|
220
222
|
:param project: project to initiate the context in (by default `mlrun.mlconf.default_project`)
|
|
221
223
|
:param upload_artifacts: when using local context (not as part of a job/run), upload artifacts to the
|
|
222
224
|
system default artifact path location
|
|
223
|
-
:param labels:
|
|
225
|
+
:param labels: (deprecated - use spec instead) dict of the context labels.
|
|
224
226
|
:return: execution context
|
|
225
227
|
|
|
226
228
|
Examples::
|
|
@@ -253,6 +255,20 @@ def get_or_create_ctx(
|
|
|
253
255
|
context.log_artifact("results.html", body=b"<b> Some HTML <b>", viewer="web-app")
|
|
254
256
|
|
|
255
257
|
"""
|
|
258
|
+
if labels:
|
|
259
|
+
warnings.warn(
|
|
260
|
+
"The `labels` argument is deprecated and will be removed in 1.9.0. "
|
|
261
|
+
"Please use `spec` instead, e.g.:\n"
|
|
262
|
+
"spec={'metadata': {'labels': {'key': 'value'}}}",
|
|
263
|
+
FutureWarning,
|
|
264
|
+
)
|
|
265
|
+
if spec is None:
|
|
266
|
+
spec = {}
|
|
267
|
+
if "metadata" not in spec:
|
|
268
|
+
spec["metadata"] = {}
|
|
269
|
+
if "labels" not in spec["metadata"]:
|
|
270
|
+
spec["metadata"]["labels"] = {}
|
|
271
|
+
spec["metadata"]["labels"].update(labels)
|
|
256
272
|
|
|
257
273
|
if global_context.get() and not spec and not event:
|
|
258
274
|
return global_context.get()
|
|
@@ -306,9 +322,6 @@ def get_or_create_ctx(
|
|
|
306
322
|
ctx = MLClientCtx.from_dict(
|
|
307
323
|
newspec, rundb=out, autocommit=autocommit, tmp=tmp, host=socket.gethostname()
|
|
308
324
|
)
|
|
309
|
-
labels = labels or {}
|
|
310
|
-
for key, val in labels.items():
|
|
311
|
-
ctx.set_label(key=key, value=val)
|
|
312
325
|
global_context.set(ctx)
|
|
313
326
|
return ctx
|
|
314
327
|
|
|
@@ -326,6 +326,11 @@ class APIGatewaySpec(ModelObj):
|
|
|
326
326
|
return function_names
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
class APIGatewayStatus(ModelObj):
|
|
330
|
+
def __init__(self, state: Optional[schemas.APIGatewayState] = None):
|
|
331
|
+
self.state = state or schemas.APIGatewayState.none
|
|
332
|
+
|
|
333
|
+
|
|
329
334
|
class APIGateway(ModelObj):
|
|
330
335
|
_dict_fields = [
|
|
331
336
|
"metadata",
|
|
@@ -338,16 +343,18 @@ class APIGateway(ModelObj):
|
|
|
338
343
|
self,
|
|
339
344
|
metadata: APIGatewayMetadata,
|
|
340
345
|
spec: APIGatewaySpec,
|
|
346
|
+
status: Optional[APIGatewayStatus] = None,
|
|
341
347
|
):
|
|
342
348
|
"""
|
|
343
349
|
Initialize the APIGateway instance.
|
|
344
350
|
|
|
345
351
|
:param metadata: (APIGatewayMetadata) The metadata of the API gateway.
|
|
346
352
|
:param spec: (APIGatewaySpec) The spec of the API gateway.
|
|
353
|
+
:param status: (APIGatewayStatus) The status of the API gateway.
|
|
347
354
|
"""
|
|
348
355
|
self.metadata = metadata
|
|
349
356
|
self.spec = spec
|
|
350
|
-
self.
|
|
357
|
+
self.status = status
|
|
351
358
|
|
|
352
359
|
@property
|
|
353
360
|
def metadata(self) -> APIGatewayMetadata:
|
|
@@ -365,6 +372,14 @@ class APIGateway(ModelObj):
|
|
|
365
372
|
def spec(self, spec):
|
|
366
373
|
self._spec = self._verify_dict(spec, "spec", APIGatewaySpec)
|
|
367
374
|
|
|
375
|
+
@property
|
|
376
|
+
def status(self) -> APIGatewayStatus:
|
|
377
|
+
return self._status
|
|
378
|
+
|
|
379
|
+
@status.setter
|
|
380
|
+
def status(self, status):
|
|
381
|
+
self._status = self._verify_dict(status, "status", APIGatewayStatus)
|
|
382
|
+
|
|
368
383
|
def invoke(
|
|
369
384
|
self,
|
|
370
385
|
method="POST",
|
|
@@ -394,7 +409,7 @@ class APIGateway(ModelObj):
|
|
|
394
409
|
)
|
|
395
410
|
if not self.is_ready():
|
|
396
411
|
raise mlrun.errors.MLRunPreconditionFailedError(
|
|
397
|
-
f"API gateway is not ready. " f"Current state: {self.state}"
|
|
412
|
+
f"API gateway is not ready. " f"Current state: {self.status.state}"
|
|
398
413
|
)
|
|
399
414
|
|
|
400
415
|
auth = None
|
|
@@ -459,10 +474,10 @@ class APIGateway(ModelObj):
|
|
|
459
474
|
)
|
|
460
475
|
|
|
461
476
|
def is_ready(self):
|
|
462
|
-
if self.state is not schemas.api_gateway.APIGatewayState.ready:
|
|
477
|
+
if self.status.state is not schemas.api_gateway.APIGatewayState.ready:
|
|
463
478
|
# try to sync the state
|
|
464
479
|
self.sync()
|
|
465
|
-
return self.state == schemas.api_gateway.APIGatewayState.ready
|
|
480
|
+
return self.status.state == schemas.api_gateway.APIGatewayState.ready
|
|
466
481
|
|
|
467
482
|
def sync(self):
|
|
468
483
|
"""
|
|
@@ -479,7 +494,7 @@ class APIGateway(ModelObj):
|
|
|
479
494
|
self.spec.functions = synced_gateway.spec.functions
|
|
480
495
|
self.spec.canary = synced_gateway.spec.canary
|
|
481
496
|
self.spec.description = synced_gateway.spec.description
|
|
482
|
-
self.state = synced_gateway.state
|
|
497
|
+
self.status.state = synced_gateway.status.state
|
|
483
498
|
|
|
484
499
|
def with_basic_auth(self, username: str, password: str):
|
|
485
500
|
"""
|
|
@@ -546,6 +561,14 @@ class APIGateway(ModelObj):
|
|
|
546
561
|
project=self.spec.project, functions=self.spec.functions, ports=ports
|
|
547
562
|
)
|
|
548
563
|
|
|
564
|
+
def with_force_ssl_redirect(self):
|
|
565
|
+
"""
|
|
566
|
+
Set SSL redirect annotation for the API gateway.
|
|
567
|
+
"""
|
|
568
|
+
self.metadata.annotations["nginx.ingress.kubernetes.io/force-ssl-redirect"] = (
|
|
569
|
+
"true"
|
|
570
|
+
)
|
|
571
|
+
|
|
549
572
|
@classmethod
|
|
550
573
|
def from_scheme(cls, api_gateway: schemas.APIGateway):
|
|
551
574
|
project = api_gateway.metadata.labels.get(
|
|
@@ -560,6 +583,8 @@ class APIGateway(ModelObj):
|
|
|
560
583
|
new_api_gateway = cls(
|
|
561
584
|
metadata=APIGatewayMetadata(
|
|
562
585
|
name=api_gateway.spec.name,
|
|
586
|
+
annotations=api_gateway.metadata.annotations,
|
|
587
|
+
labels=api_gateway.metadata.labels,
|
|
563
588
|
),
|
|
564
589
|
spec=APIGatewaySpec(
|
|
565
590
|
project=project,
|
|
@@ -570,8 +595,8 @@ class APIGateway(ModelObj):
|
|
|
570
595
|
functions=functions,
|
|
571
596
|
canary=canary,
|
|
572
597
|
),
|
|
598
|
+
status=APIGatewayStatus(state=state),
|
|
573
599
|
)
|
|
574
|
-
new_api_gateway.state = state
|
|
575
600
|
return new_api_gateway
|
|
576
601
|
|
|
577
602
|
def to_scheme(self) -> schemas.APIGateway:
|
|
@@ -600,7 +625,11 @@ class APIGateway(ModelObj):
|
|
|
600
625
|
upstreams[i].port = port
|
|
601
626
|
|
|
602
627
|
api_gateway = schemas.APIGateway(
|
|
603
|
-
metadata=schemas.APIGatewayMetadata(
|
|
628
|
+
metadata=schemas.APIGatewayMetadata(
|
|
629
|
+
name=self.metadata.name,
|
|
630
|
+
labels=self.metadata.labels,
|
|
631
|
+
annotations=self.metadata.annotations,
|
|
632
|
+
),
|
|
604
633
|
spec=schemas.APIGatewaySpec(
|
|
605
634
|
name=self.metadata.name,
|
|
606
635
|
description=self.spec.description,
|
|
@@ -611,6 +640,7 @@ class APIGateway(ModelObj):
|
|
|
611
640
|
),
|
|
612
641
|
upstreams=upstreams,
|
|
613
642
|
),
|
|
643
|
+
status=schemas.APIGatewayStatus(state=self.status.state),
|
|
614
644
|
)
|
|
615
645
|
api_gateway.spec.authentication = self.spec.authentication.to_scheme()
|
|
616
646
|
return api_gateway
|
|
@@ -149,6 +149,7 @@ class ApplicationStatus(NuclioStatus):
|
|
|
149
149
|
build_pod=None,
|
|
150
150
|
container_image=None,
|
|
151
151
|
application_image=None,
|
|
152
|
+
application_source=None,
|
|
152
153
|
sidecar_name=None,
|
|
153
154
|
api_gateway_name=None,
|
|
154
155
|
api_gateway=None,
|
|
@@ -164,6 +165,7 @@ class ApplicationStatus(NuclioStatus):
|
|
|
164
165
|
container_image=container_image,
|
|
165
166
|
)
|
|
166
167
|
self.application_image = application_image or None
|
|
168
|
+
self.application_source = application_source or None
|
|
167
169
|
self.sidecar_name = sidecar_name or None
|
|
168
170
|
self.api_gateway_name = api_gateway_name or None
|
|
169
171
|
self.api_gateway = api_gateway or None
|
|
@@ -266,6 +268,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
266
268
|
direct_port_access: bool = False,
|
|
267
269
|
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
268
270
|
authentication_creds: tuple[str] = None,
|
|
271
|
+
ssl_redirect: bool = None,
|
|
269
272
|
):
|
|
270
273
|
"""
|
|
271
274
|
Deploy function, builds the application image if required (self.requires_build()) or force_build is True,
|
|
@@ -285,6 +288,9 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
285
288
|
:param direct_port_access: Set True to allow direct port access to the application sidecar
|
|
286
289
|
:param authentication_mode: API Gateway authentication mode
|
|
287
290
|
:param authentication_creds: API Gateway authentication credentials as a tuple (username, password)
|
|
291
|
+
:param ssl_redirect: Set True to force SSL redirect, False to disable. Defaults to
|
|
292
|
+
mlrun.mlconf.force_api_gateway_ssl_redirect()
|
|
293
|
+
|
|
288
294
|
:return: True if the function is ready (deployed)
|
|
289
295
|
"""
|
|
290
296
|
if self.requires_build() or force_build:
|
|
@@ -313,19 +319,30 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
313
319
|
)
|
|
314
320
|
|
|
315
321
|
super().deploy(
|
|
316
|
-
project,
|
|
317
|
-
tag,
|
|
318
|
-
verbose,
|
|
319
|
-
auth_info,
|
|
320
|
-
builder_env,
|
|
322
|
+
project=project,
|
|
323
|
+
tag=tag,
|
|
324
|
+
verbose=verbose,
|
|
325
|
+
auth_info=auth_info,
|
|
326
|
+
builder_env=builder_env,
|
|
327
|
+
)
|
|
328
|
+
logger.info(
|
|
329
|
+
"Successfully deployed function, creating API gateway",
|
|
330
|
+
api_gateway_name=self.status.api_gateway_name,
|
|
331
|
+
authentication_mode=authentication_mode,
|
|
321
332
|
)
|
|
322
333
|
|
|
334
|
+
# Restore the source in case it was removed to make nuclio not consider it when building
|
|
335
|
+
if not self.spec.build.source and self.status.application_source:
|
|
336
|
+
self.spec.build.source = self.status.application_source
|
|
337
|
+
self.save(versioned=False)
|
|
338
|
+
|
|
323
339
|
ports = self.spec.internal_application_port if direct_port_access else []
|
|
324
|
-
self.create_api_gateway(
|
|
340
|
+
return self.create_api_gateway(
|
|
325
341
|
name=self.status.api_gateway_name,
|
|
326
342
|
ports=ports,
|
|
327
343
|
authentication_mode=authentication_mode,
|
|
328
344
|
authentication_creds=authentication_creds,
|
|
345
|
+
ssl_redirect=ssl_redirect,
|
|
329
346
|
)
|
|
330
347
|
|
|
331
348
|
def with_source_archive(
|
|
@@ -349,6 +366,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
349
366
|
target_dir=target_dir,
|
|
350
367
|
)
|
|
351
368
|
|
|
369
|
+
def from_image(self, image):
|
|
370
|
+
super().from_image(image)
|
|
371
|
+
# nuclio implementation detail - when providing the image and emptying out the source code and build source,
|
|
372
|
+
# nuclio skips rebuilding the image and simply takes the prebuilt image
|
|
373
|
+
self.spec.build.functionSourceCode = ""
|
|
374
|
+
self.status.application_source = self.spec.build.source
|
|
375
|
+
self.spec.build.source = ""
|
|
376
|
+
|
|
352
377
|
@classmethod
|
|
353
378
|
def get_filename_and_handler(cls) -> (str, str):
|
|
354
379
|
reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
|
|
@@ -361,6 +386,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
361
386
|
ports: list[int] = None,
|
|
362
387
|
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
363
388
|
authentication_creds: tuple[str] = None,
|
|
389
|
+
ssl_redirect: bool = None,
|
|
364
390
|
):
|
|
365
391
|
api_gateway = APIGateway(
|
|
366
392
|
APIGatewayMetadata(
|
|
@@ -377,6 +403,13 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
377
403
|
),
|
|
378
404
|
)
|
|
379
405
|
|
|
406
|
+
if ssl_redirect is None:
|
|
407
|
+
ssl_redirect = mlrun.mlconf.force_api_gateway_ssl_redirect()
|
|
408
|
+
if ssl_redirect:
|
|
409
|
+
# force ssl redirect so that the application is only accessible via https
|
|
410
|
+
api_gateway.with_force_ssl_redirect()
|
|
411
|
+
|
|
412
|
+
# add authentication if required
|
|
380
413
|
authentication_mode = (
|
|
381
414
|
authentication_mode
|
|
382
415
|
or mlrun.mlconf.function.application.default_authentication_mode
|
|
@@ -396,6 +429,9 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
396
429
|
self.status.api_gateway.wait_for_readiness()
|
|
397
430
|
self.url = self.status.api_gateway.invoke_url
|
|
398
431
|
|
|
432
|
+
logger.info("Successfully created API gateway", url=self.url)
|
|
433
|
+
return self.url
|
|
434
|
+
|
|
399
435
|
def invoke(
|
|
400
436
|
self,
|
|
401
437
|
path: str,
|
|
@@ -435,6 +471,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
435
471
|
**http_client_kwargs,
|
|
436
472
|
)
|
|
437
473
|
|
|
474
|
+
def _run(self, runobj: "mlrun.RunObject", execution):
|
|
475
|
+
raise mlrun.runtimes.RunError(
|
|
476
|
+
"Application runtime .run() is not yet supported. Use .invoke() instead."
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
def _enrich_command_from_status(self):
|
|
480
|
+
pass
|
|
481
|
+
|
|
438
482
|
def _build_application_image(
|
|
439
483
|
self,
|
|
440
484
|
builder_env: dict = None,
|
|
@@ -493,9 +537,6 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
493
537
|
|
|
494
538
|
if self.status.container_image:
|
|
495
539
|
self.from_image(self.status.container_image)
|
|
496
|
-
# nuclio implementation detail - when providing the image and emptying out the source code,
|
|
497
|
-
# nuclio skips rebuilding the image and simply takes the prebuilt image
|
|
498
|
-
self.spec.build.functionSourceCode = ""
|
|
499
540
|
|
|
500
541
|
self.status.sidecar_name = f"{self.metadata.name}-sidecar"
|
|
501
542
|
self.with_sidecar(
|
|
@@ -568,6 +568,9 @@ class RemoteRuntime(KubeResource):
|
|
|
568
568
|
# this also means that the function object will be updated with the function status
|
|
569
569
|
self._wait_for_function_deployment(db, verbose=verbose)
|
|
570
570
|
|
|
571
|
+
return self._enrich_command_from_status()
|
|
572
|
+
|
|
573
|
+
def _enrich_command_from_status(self):
|
|
571
574
|
# NOTE: on older mlrun versions & nuclio versions, function are exposed via NodePort
|
|
572
575
|
# now, functions can be not exposed (using service type ClusterIP) and hence
|
|
573
576
|
# for BC we first try to populate the external invocation url, and then
|
mlrun/runtimes/nuclio/serving.py
CHANGED
|
@@ -325,12 +325,12 @@ class ServingRuntime(RemoteRuntime):
|
|
|
325
325
|
:param enable_tracking: Enabled/Disable model-monitoring tracking.
|
|
326
326
|
Default True (tracking enabled).
|
|
327
327
|
|
|
328
|
-
|
|
328
|
+
Example::
|
|
329
329
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
330
|
+
# initialize a new serving function
|
|
331
|
+
serving_fn = mlrun.import_function("hub://v2-model-server", new_name="serving")
|
|
332
|
+
# apply model monitoring
|
|
333
|
+
serving_fn.set_tracking()
|
|
334
334
|
|
|
335
335
|
"""
|
|
336
336
|
# Applying model monitoring configurations
|
mlrun/serving/server.py
CHANGED
|
@@ -321,9 +321,9 @@ def v2_serving_init(context, namespace=None):
|
|
|
321
321
|
server.http_trigger = getattr(context.trigger, "kind", "http") == "http"
|
|
322
322
|
context.logger.info_with(
|
|
323
323
|
"Setting current function",
|
|
324
|
-
|
|
324
|
+
current_function=os.getenv("SERVING_CURRENT_FUNCTION", ""),
|
|
325
325
|
)
|
|
326
|
-
server.set_current_function(os.
|
|
326
|
+
server.set_current_function(os.getenv("SERVING_CURRENT_FUNCTION", ""))
|
|
327
327
|
context.logger.info_with(
|
|
328
328
|
"Initializing states", namespace=namespace or get_caller_globals()
|
|
329
329
|
)
|
|
@@ -344,9 +344,14 @@ def v2_serving_init(context, namespace=None):
|
|
|
344
344
|
if server.verbose:
|
|
345
345
|
context.logger.info(server.to_yaml())
|
|
346
346
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
347
|
+
_set_callbacks(server, context)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _set_callbacks(server, context):
|
|
351
|
+
if not server.graph.supports_termination() or not hasattr(context, "platform"):
|
|
352
|
+
return
|
|
353
|
+
|
|
354
|
+
if hasattr(context.platform, "set_termination_callback"):
|
|
350
355
|
context.logger.info(
|
|
351
356
|
"Setting termination callback to terminate graph on worker shutdown"
|
|
352
357
|
)
|
|
@@ -358,7 +363,7 @@ def v2_serving_init(context, namespace=None):
|
|
|
358
363
|
|
|
359
364
|
context.platform.set_termination_callback(termination_callback)
|
|
360
365
|
|
|
361
|
-
if hasattr(context
|
|
366
|
+
if hasattr(context.platform, "set_drain_callback"):
|
|
362
367
|
context.logger.info(
|
|
363
368
|
"Setting drain callback to terminate and restart the graph on a drain event (such as rebalancing)"
|
|
364
369
|
)
|
|
@@ -417,7 +422,7 @@ def create_graph_server(
|
|
|
417
422
|
parameters = parameters or {}
|
|
418
423
|
server = GraphServer(graph, parameters, load_mode, verbose=verbose, **kwargs)
|
|
419
424
|
server.set_current_function(
|
|
420
|
-
current_function or os.
|
|
425
|
+
current_function or os.getenv("SERVING_CURRENT_FUNCTION", "")
|
|
421
426
|
)
|
|
422
427
|
return server
|
|
423
428
|
|
mlrun/serving/states.py
CHANGED
|
@@ -27,6 +27,8 @@ from copy import copy, deepcopy
|
|
|
27
27
|
from inspect import getfullargspec, signature
|
|
28
28
|
from typing import Any, Union
|
|
29
29
|
|
|
30
|
+
import storey.utils
|
|
31
|
+
|
|
30
32
|
import mlrun
|
|
31
33
|
|
|
32
34
|
from ..config import config
|
|
@@ -386,6 +388,9 @@ class BaseStep(ModelObj):
|
|
|
386
388
|
"""
|
|
387
389
|
raise NotImplementedError("set_flow() can only be called on a FlowStep")
|
|
388
390
|
|
|
391
|
+
def supports_termination(self):
|
|
392
|
+
return False
|
|
393
|
+
|
|
389
394
|
|
|
390
395
|
class TaskStep(BaseStep):
|
|
391
396
|
"""task execution step, runs a class or handler"""
|
|
@@ -867,7 +872,9 @@ class QueueStep(BaseStep):
|
|
|
867
872
|
return event
|
|
868
873
|
|
|
869
874
|
if self._stream:
|
|
870
|
-
self.
|
|
875
|
+
if self.options.get("full_event", True):
|
|
876
|
+
data = storey.utils.wrap_event_for_serialization(event, data)
|
|
877
|
+
self._stream.push(data)
|
|
871
878
|
event.terminated = True
|
|
872
879
|
event.body = None
|
|
873
880
|
return event
|
|
@@ -1273,6 +1280,8 @@ class FlowStep(BaseStep):
|
|
|
1273
1280
|
event.body = {"id": event.id}
|
|
1274
1281
|
return event
|
|
1275
1282
|
|
|
1283
|
+
event = storey.utils.unpack_event_if_wrapped(event)
|
|
1284
|
+
|
|
1276
1285
|
if len(self._start_steps) == 0:
|
|
1277
1286
|
return event
|
|
1278
1287
|
next_obj = self._start_steps[0]
|
|
@@ -1380,6 +1389,9 @@ class FlowStep(BaseStep):
|
|
|
1380
1389
|
|
|
1381
1390
|
return step
|
|
1382
1391
|
|
|
1392
|
+
def supports_termination(self):
|
|
1393
|
+
return self.engine == "async"
|
|
1394
|
+
|
|
1383
1395
|
|
|
1384
1396
|
class RootFlowStep(FlowStep):
|
|
1385
1397
|
"""root flow step"""
|
mlrun/utils/db.py
CHANGED
|
@@ -28,6 +28,9 @@ class BaseModel:
|
|
|
28
28
|
columns = [column.key for column in mapper.columns if column.key not in exclude]
|
|
29
29
|
|
|
30
30
|
def get_key_value(c):
|
|
31
|
+
# all (never say never) DB classes have "object" defined as "full_object"
|
|
32
|
+
if c == "object":
|
|
33
|
+
c = "full_object"
|
|
31
34
|
if isinstance(getattr(self, c), datetime):
|
|
32
35
|
return c, getattr(self, c).isoformat()
|
|
33
36
|
return c, getattr(self, c)
|
mlrun/utils/helpers.py
CHANGED
|
@@ -111,13 +111,11 @@ def get_artifact_target(item: dict, project=None):
|
|
|
111
111
|
tree = item["metadata"].get("tree")
|
|
112
112
|
tag = item["metadata"].get("tag")
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
if kind in ["dataset", "model", "artifact"] and db_key:
|
|
114
|
+
if item.get("kind") in {"dataset", "model", "artifact"} and db_key:
|
|
116
115
|
target = f"{DB_SCHEMA}://{StorePrefix.Artifact}/{project_str}/{db_key}"
|
|
117
|
-
if tag:
|
|
118
|
-
target = f"{target}:{tag}"
|
|
116
|
+
target += f":{tag}" if tag else ":latest"
|
|
119
117
|
if tree:
|
|
120
|
-
target
|
|
118
|
+
target += f"@{tree}"
|
|
121
119
|
return target
|
|
122
120
|
|
|
123
121
|
return item["spec"].get("target_path")
|
|
@@ -60,7 +60,14 @@ class WebhookNotification(NotificationBase):
|
|
|
60
60
|
request_body["runs"] = runs
|
|
61
61
|
|
|
62
62
|
if alert:
|
|
63
|
-
request_body["
|
|
63
|
+
request_body["name"] = alert.name
|
|
64
|
+
request_body["project"] = alert.project
|
|
65
|
+
request_body["severity"] = alert.severity
|
|
66
|
+
if alert.summary:
|
|
67
|
+
request_body["summary"] = mlrun.utils.helpers.format_alert_summary(
|
|
68
|
+
alert, event_data
|
|
69
|
+
)
|
|
70
|
+
|
|
64
71
|
if event_data:
|
|
65
72
|
request_body["value"] = event_data.value_dict
|
|
66
73
|
request_body["id"] = event_data.entity.ids[0]
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mlrun
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.0rc34
|
|
4
4
|
Summary: Tracking and config of machine learning runs
|
|
5
5
|
Home-page: https://github.com/mlrun/mlrun
|
|
6
6
|
Author: Yaron Haviv
|
|
@@ -43,10 +43,10 @@ Requires-Dist: semver ~=3.0
|
|
|
43
43
|
Requires-Dist: dependency-injector ~=4.41
|
|
44
44
|
Requires-Dist: fsspec <2024.4,>=2023.9.2
|
|
45
45
|
Requires-Dist: v3iofs ~=0.1.17
|
|
46
|
-
Requires-Dist: storey ~=1.7.
|
|
46
|
+
Requires-Dist: storey ~=1.7.23
|
|
47
47
|
Requires-Dist: inflection ~=0.5.0
|
|
48
48
|
Requires-Dist: python-dotenv ~=0.17.0
|
|
49
|
-
Requires-Dist: setuptools ~=
|
|
49
|
+
Requires-Dist: setuptools ~=71.0
|
|
50
50
|
Requires-Dist: deprecated ~=1.2
|
|
51
51
|
Requires-Dist: jinja2 >=3.1.3,~=3.1
|
|
52
52
|
Requires-Dist: orjson <4,>=3.9.15
|