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.

Files changed (35) hide show
  1. mlrun/common/schemas/__init__.py +1 -0
  2. mlrun/common/schemas/common.py +3 -0
  3. mlrun/common/schemas/function.py +7 -0
  4. mlrun/common/schemas/project.py +35 -3
  5. mlrun/config.py +9 -1
  6. mlrun/datastore/base.py +5 -1
  7. mlrun/db/base.py +8 -3
  8. mlrun/db/httpdb.py +10 -8
  9. mlrun/db/nopdb.py +3 -1
  10. mlrun/execution.py +1 -3
  11. mlrun/model.py +142 -22
  12. mlrun/model_monitoring/applications/context.py +13 -15
  13. mlrun/model_monitoring/controller.py +1 -1
  14. mlrun/model_monitoring/db/stores/base/store.py +2 -0
  15. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +9 -23
  16. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +5 -20
  17. mlrun/model_monitoring/stream_processing.py +6 -0
  18. mlrun/projects/project.py +8 -2
  19. mlrun/run.py +22 -9
  20. mlrun/runtimes/nuclio/api_gateway.py +37 -7
  21. mlrun/runtimes/nuclio/application/application.py +50 -9
  22. mlrun/runtimes/nuclio/function.py +3 -0
  23. mlrun/runtimes/nuclio/serving.py +5 -5
  24. mlrun/serving/server.py +12 -7
  25. mlrun/serving/states.py +13 -1
  26. mlrun/utils/db.py +3 -0
  27. mlrun/utils/helpers.py +3 -5
  28. mlrun/utils/notifications/notification/webhook.py +8 -1
  29. mlrun/utils/version/version.json +2 -2
  30. {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/METADATA +3 -3
  31. {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/RECORD +35 -35
  32. {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/WHEEL +1 -1
  33. {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/LICENSE +0 -0
  34. {mlrun-1.7.0rc32.dist-info → mlrun-1.7.0rc34.dist-info}/entry_points.txt +0 -0
  35. {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 in version 1.9.0."
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
- """called from within the user program to obtain a run context
207
+ """
208
+ Called from within the user program to obtain a run context.
207
209
 
208
- the run context is an interface for receiving parameters, data and logging
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: dict of the context 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.state = ""
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(name=self.metadata.name, labels={}),
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
@@ -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
- example::
328
+ Example::
329
329
 
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()
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
- current_functiton=os.environ.get("SERVING_CURRENT_FUNCTION", ""),
324
+ current_function=os.getenv("SERVING_CURRENT_FUNCTION", ""),
325
325
  )
326
- server.set_current_function(os.environ.get("SERVING_CURRENT_FUNCTION", ""))
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
- if hasattr(context, "platform") and hasattr(
348
- context.platform, "set_termination_callback"
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, "platform") and hasattr(context.platform, "set_drain_callback"):
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.environ.get("SERVING_CURRENT_FUNCTION", "")
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._stream.push({"id": event.id, "body": data, "path": event.path})
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
- kind = item.get("kind")
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 = f"{target}@{tree}"
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["alert"] = alert.dict()
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]
@@ -1,4 +1,4 @@
1
1
  {
2
- "git_commit": "8180ea5cac9bb9ed6be04b85fd1ba9a79f3a9f9a",
3
- "version": "1.7.0-rc32"
2
+ "git_commit": "4542af29f22f596b4e09be897f728a6e8f676b55",
3
+ "version": "1.7.0-rc34"
4
4
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mlrun
3
- Version: 1.7.0rc32
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.20
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 ~=70.0
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