mlrun 1.7.0rc28__py3-none-any.whl → 1.7.0rc55__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 (135) hide show
  1. mlrun/__main__.py +4 -2
  2. mlrun/alerts/alert.py +75 -8
  3. mlrun/artifacts/base.py +1 -0
  4. mlrun/artifacts/manager.py +9 -2
  5. mlrun/common/constants.py +4 -1
  6. mlrun/common/db/sql_session.py +3 -2
  7. mlrun/common/formatters/__init__.py +1 -0
  8. mlrun/common/formatters/artifact.py +1 -0
  9. mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
  10. mlrun/common/formatters/run.py +3 -0
  11. mlrun/common/helpers.py +0 -1
  12. mlrun/common/schemas/__init__.py +3 -1
  13. mlrun/common/schemas/alert.py +15 -12
  14. mlrun/common/schemas/api_gateway.py +6 -6
  15. mlrun/common/schemas/auth.py +5 -0
  16. mlrun/common/schemas/client_spec.py +0 -1
  17. mlrun/common/schemas/common.py +7 -4
  18. mlrun/common/schemas/frontend_spec.py +7 -0
  19. mlrun/common/schemas/function.py +7 -0
  20. mlrun/common/schemas/model_monitoring/__init__.py +4 -3
  21. mlrun/common/schemas/model_monitoring/constants.py +41 -26
  22. mlrun/common/schemas/model_monitoring/model_endpoints.py +23 -47
  23. mlrun/common/schemas/notification.py +69 -12
  24. mlrun/common/schemas/project.py +45 -12
  25. mlrun/common/schemas/workflow.py +10 -2
  26. mlrun/common/types.py +1 -0
  27. mlrun/config.py +91 -35
  28. mlrun/data_types/data_types.py +6 -1
  29. mlrun/data_types/spark.py +2 -2
  30. mlrun/data_types/to_pandas.py +57 -25
  31. mlrun/datastore/__init__.py +1 -0
  32. mlrun/datastore/alibaba_oss.py +3 -2
  33. mlrun/datastore/azure_blob.py +125 -37
  34. mlrun/datastore/base.py +42 -21
  35. mlrun/datastore/datastore.py +4 -2
  36. mlrun/datastore/datastore_profile.py +1 -1
  37. mlrun/datastore/dbfs_store.py +3 -7
  38. mlrun/datastore/filestore.py +1 -3
  39. mlrun/datastore/google_cloud_storage.py +85 -29
  40. mlrun/datastore/inmem.py +4 -1
  41. mlrun/datastore/redis.py +1 -0
  42. mlrun/datastore/s3.py +25 -12
  43. mlrun/datastore/sources.py +76 -4
  44. mlrun/datastore/spark_utils.py +30 -0
  45. mlrun/datastore/storeytargets.py +151 -0
  46. mlrun/datastore/targets.py +102 -131
  47. mlrun/datastore/v3io.py +1 -0
  48. mlrun/db/base.py +15 -6
  49. mlrun/db/httpdb.py +57 -28
  50. mlrun/db/nopdb.py +29 -5
  51. mlrun/errors.py +20 -3
  52. mlrun/execution.py +46 -5
  53. mlrun/feature_store/api.py +25 -1
  54. mlrun/feature_store/common.py +6 -11
  55. mlrun/feature_store/feature_vector.py +3 -1
  56. mlrun/feature_store/retrieval/job.py +4 -1
  57. mlrun/feature_store/retrieval/spark_merger.py +10 -39
  58. mlrun/feature_store/steps.py +8 -0
  59. mlrun/frameworks/_common/plan.py +3 -3
  60. mlrun/frameworks/_ml_common/plan.py +1 -1
  61. mlrun/frameworks/parallel_coordinates.py +2 -3
  62. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  63. mlrun/k8s_utils.py +48 -2
  64. mlrun/launcher/client.py +6 -6
  65. mlrun/launcher/local.py +2 -2
  66. mlrun/model.py +215 -34
  67. mlrun/model_monitoring/api.py +38 -24
  68. mlrun/model_monitoring/applications/__init__.py +1 -2
  69. mlrun/model_monitoring/applications/_application_steps.py +60 -29
  70. mlrun/model_monitoring/applications/base.py +2 -174
  71. mlrun/model_monitoring/applications/context.py +197 -70
  72. mlrun/model_monitoring/applications/evidently_base.py +11 -85
  73. mlrun/model_monitoring/applications/histogram_data_drift.py +21 -16
  74. mlrun/model_monitoring/applications/results.py +4 -4
  75. mlrun/model_monitoring/controller.py +110 -282
  76. mlrun/model_monitoring/db/stores/__init__.py +8 -3
  77. mlrun/model_monitoring/db/stores/base/store.py +3 -0
  78. mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
  79. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +18 -3
  80. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +43 -23
  81. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +48 -35
  82. mlrun/model_monitoring/db/tsdb/__init__.py +7 -2
  83. mlrun/model_monitoring/db/tsdb/base.py +147 -15
  84. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +94 -55
  85. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -3
  86. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +144 -38
  87. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +44 -3
  88. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +246 -57
  89. mlrun/model_monitoring/helpers.py +70 -50
  90. mlrun/model_monitoring/stream_processing.py +96 -195
  91. mlrun/model_monitoring/writer.py +13 -5
  92. mlrun/package/packagers/default_packager.py +2 -2
  93. mlrun/projects/operations.py +16 -8
  94. mlrun/projects/pipelines.py +126 -115
  95. mlrun/projects/project.py +286 -129
  96. mlrun/render.py +3 -3
  97. mlrun/run.py +38 -19
  98. mlrun/runtimes/__init__.py +19 -8
  99. mlrun/runtimes/base.py +4 -1
  100. mlrun/runtimes/daskjob.py +1 -1
  101. mlrun/runtimes/funcdoc.py +1 -1
  102. mlrun/runtimes/kubejob.py +6 -6
  103. mlrun/runtimes/local.py +12 -5
  104. mlrun/runtimes/nuclio/api_gateway.py +68 -8
  105. mlrun/runtimes/nuclio/application/application.py +307 -70
  106. mlrun/runtimes/nuclio/function.py +63 -14
  107. mlrun/runtimes/nuclio/serving.py +10 -10
  108. mlrun/runtimes/pod.py +25 -19
  109. mlrun/runtimes/remotesparkjob.py +2 -5
  110. mlrun/runtimes/sparkjob/spark3job.py +16 -17
  111. mlrun/runtimes/utils.py +34 -0
  112. mlrun/serving/routers.py +2 -5
  113. mlrun/serving/server.py +37 -19
  114. mlrun/serving/states.py +30 -3
  115. mlrun/serving/v2_serving.py +44 -35
  116. mlrun/track/trackers/mlflow_tracker.py +5 -0
  117. mlrun/utils/async_http.py +1 -1
  118. mlrun/utils/db.py +18 -0
  119. mlrun/utils/helpers.py +150 -36
  120. mlrun/utils/http.py +1 -1
  121. mlrun/utils/notifications/notification/__init__.py +0 -1
  122. mlrun/utils/notifications/notification/webhook.py +8 -1
  123. mlrun/utils/notifications/notification_pusher.py +1 -1
  124. mlrun/utils/v3io_clients.py +2 -2
  125. mlrun/utils/version/version.json +2 -2
  126. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +153 -66
  127. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +131 -134
  128. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
  129. mlrun/feature_store/retrieval/conversion.py +0 -271
  130. mlrun/model_monitoring/controller_handler.py +0 -37
  131. mlrun/model_monitoring/evidently_application.py +0 -20
  132. mlrun/model_monitoring/prometheus.py +0 -216
  133. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
  134. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
  135. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py CHANGED
@@ -18,6 +18,7 @@ import glob
18
18
  import http
19
19
  import importlib.util as imputil
20
20
  import json
21
+ import os
21
22
  import pathlib
22
23
  import shutil
23
24
  import tempfile
@@ -25,6 +26,7 @@ import typing
25
26
  import uuid
26
27
  import warnings
27
28
  import zipfile
29
+ from copy import deepcopy
28
30
  from os import environ, makedirs, path
29
31
  from typing import Callable, Optional, Union
30
32
 
@@ -65,13 +67,7 @@ from ..features import Feature
65
67
  from ..model import EntrypointParam, ImageBuilder, ModelObj
66
68
  from ..run import code_to_function, get_object, import_function, new_function
67
69
  from ..secrets import SecretsStore
68
- from ..utils import (
69
- is_ipython,
70
- is_relative_path,
71
- is_yaml_path,
72
- logger,
73
- update_in,
74
- )
70
+ from ..utils import is_jupyter, is_relative_path, is_yaml_path, logger, update_in
75
71
  from ..utils.clones import (
76
72
  add_credentials_git_remote_url,
77
73
  clone_git,
@@ -251,8 +247,7 @@ def new_project(
251
247
  project.spec.description = description
252
248
 
253
249
  if default_function_node_selector:
254
- for key, val in default_function_node_selector.items():
255
- project.spec.default_function_node_selector[key] = val
250
+ project.spec.default_function_node_selector = default_function_node_selector
256
251
 
257
252
  if parameters:
258
253
  # Enable setting project parameters at load time, can be used to customize the project_setup
@@ -517,17 +512,24 @@ def get_or_create_project(
517
512
  parameters=parameters,
518
513
  allow_cross_project=allow_cross_project,
519
514
  )
520
- logger.info("Project loaded successfully", project_name=name)
515
+ logger.info("Project loaded successfully", project_name=project.name)
521
516
  return project
522
517
  except mlrun.errors.MLRunNotFoundError:
523
- logger.debug("Project not found in db", project_name=name)
518
+ logger.debug(
519
+ "Project not found in db", project_name=name, user_project=user_project
520
+ )
524
521
 
525
522
  spec_path = path.join(context, subpath or "", "project.yaml")
526
523
  load_from_path = url or path.isfile(spec_path)
527
524
  # do not nest under "try" or else the exceptions raised below will be logged along with the "not found" message
528
525
  if load_from_path:
529
526
  # loads a project from archive or local project.yaml
530
- logger.info("Loading project from path", project_name=name, path=url or context)
527
+ logger.info(
528
+ "Loading project from path",
529
+ project_name=name,
530
+ user_project=user_project,
531
+ path=url or context,
532
+ )
531
533
  project = load_project(
532
534
  context,
533
535
  url,
@@ -544,7 +546,7 @@ def get_or_create_project(
544
546
 
545
547
  logger.info(
546
548
  "Project loaded successfully",
547
- project_name=name,
549
+ project_name=project.name,
548
550
  path=url or context,
549
551
  stored_in_db=save,
550
552
  )
@@ -562,7 +564,9 @@ def get_or_create_project(
562
564
  save=save,
563
565
  parameters=parameters,
564
566
  )
565
- logger.info("Project created successfully", project_name=name, stored_in_db=save)
567
+ logger.info(
568
+ "Project created successfully", project_name=project.name, stored_in_db=save
569
+ )
566
570
  return project
567
571
 
568
572
 
@@ -600,6 +604,10 @@ def _run_project_setup(
600
604
  if hasattr(mod, "setup"):
601
605
  try:
602
606
  project = getattr(mod, "setup")(project)
607
+ if not project or not isinstance(project, mlrun.projects.MlrunProject):
608
+ raise ValueError(
609
+ "MLRun project_setup:setup() must return a project object"
610
+ )
603
611
  except Exception as exc:
604
612
  logger.error(
605
613
  "Failed to run project_setup script",
@@ -610,7 +618,9 @@ def _run_project_setup(
610
618
  if save:
611
619
  project.save()
612
620
  else:
613
- logger.warn("skipping setup, setup() handler was not found in project_setup.py")
621
+ logger.warn(
622
+ f"skipping setup, setup() handler was not found in {path.basename(setup_file_path)}"
623
+ )
614
624
  return project
615
625
 
616
626
 
@@ -698,7 +708,7 @@ def _load_project_from_db(url, secrets, user_project=False):
698
708
 
699
709
  def _delete_project_from_db(project_name, secrets, deletion_strategy):
700
710
  db = mlrun.db.get_run_db(secrets=secrets)
701
- return db.delete_project(project_name, deletion_strategy=deletion_strategy)
711
+ db.delete_project(project_name, deletion_strategy=deletion_strategy)
702
712
 
703
713
 
704
714
  def _load_project_file(url, name="", secrets=None, allow_cross_project=None):
@@ -725,7 +735,7 @@ def _project_instance_from_struct(struct, name, allow_cross_project):
725
735
  # TODO: Remove this warning in version 1.9.0 and also fix cli to support allow_cross_project
726
736
  warnings.warn(
727
737
  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."
738
+ "This behavior is deprecated and will not be supported from version 1.9.0."
729
739
  )
730
740
  logger.warn(error_message)
731
741
  elif allow_cross_project:
@@ -859,7 +869,7 @@ class ProjectSpec(ModelObj):
859
869
  # in a tuple where the first index is the packager module's path (str) and the second is a flag (bool) for
860
870
  # whether it is mandatory for a run (raise exception on collection error) or not.
861
871
  self.custom_packagers = custom_packagers or []
862
- self.default_function_node_selector = default_function_node_selector or {}
872
+ self._default_function_node_selector = default_function_node_selector or None
863
873
 
864
874
  @property
865
875
  def source(self) -> str:
@@ -1034,6 +1044,14 @@ class ProjectSpec(ModelObj):
1034
1044
  if key in self._artifacts:
1035
1045
  del self._artifacts[key]
1036
1046
 
1047
+ @property
1048
+ def default_function_node_selector(self):
1049
+ return self._default_function_node_selector
1050
+
1051
+ @default_function_node_selector.setter
1052
+ def default_function_node_selector(self, node_selector: dict[str, str]):
1053
+ self._default_function_node_selector = deepcopy(node_selector)
1054
+
1037
1055
  @property
1038
1056
  def build(self) -> ImageBuilder:
1039
1057
  return self._build
@@ -1534,7 +1552,7 @@ class MlrunProject(ModelObj):
1534
1552
  url = path.normpath(path.join(self.spec.get_code_path(), url))
1535
1553
 
1536
1554
  if (not in_context or check_path_in_context) and not path.isfile(url):
1537
- raise mlrun.errors.MLRunNotFoundError(f"{url} not found")
1555
+ raise FileNotFoundError(f"{url} not found")
1538
1556
 
1539
1557
  return url, in_context
1540
1558
 
@@ -1542,15 +1560,15 @@ class MlrunProject(ModelObj):
1542
1560
  self,
1543
1561
  item,
1544
1562
  body=None,
1545
- tag="",
1546
- local_path="",
1547
- artifact_path=None,
1548
- format=None,
1549
- upload=None,
1550
- labels=None,
1551
- target_path=None,
1563
+ tag: str = "",
1564
+ local_path: str = "",
1565
+ artifact_path: Optional[str] = None,
1566
+ format: Optional[str] = None,
1567
+ upload: Optional[bool] = None,
1568
+ labels: Optional[dict[str, str]] = None,
1569
+ target_path: Optional[str] = None,
1552
1570
  **kwargs,
1553
- ):
1571
+ ) -> Artifact:
1554
1572
  """Log an output artifact and optionally upload it to datastore
1555
1573
 
1556
1574
  If the artifact already exists with the same key and tag, it will be overwritten.
@@ -1575,7 +1593,9 @@ class MlrunProject(ModelObj):
1575
1593
  :param format: artifact file format: csv, png, ..
1576
1594
  :param tag: version tag
1577
1595
  :param target_path: absolute target path (instead of using artifact_path + local_path)
1578
- :param upload: upload to datastore (default is True)
1596
+ :param upload: Whether to upload the artifact to the datastore. If not provided, and the `local_path`
1597
+ is not a directory, upload occurs by default. Directories are uploaded only when this
1598
+ flag is explicitly set to `True`.
1579
1599
  :param labels: a set of key/value labels to tag the artifact with
1580
1600
 
1581
1601
  :returns: artifact object
@@ -1649,7 +1669,7 @@ class MlrunProject(ModelObj):
1649
1669
  stats=None,
1650
1670
  target_path="",
1651
1671
  extra_data=None,
1652
- label_column: str = None,
1672
+ label_column: Optional[str] = None,
1653
1673
  **kwargs,
1654
1674
  ) -> DatasetArtifact:
1655
1675
  """
@@ -1726,15 +1746,15 @@ class MlrunProject(ModelObj):
1726
1746
  artifact_path=None,
1727
1747
  upload=None,
1728
1748
  labels=None,
1729
- inputs: list[Feature] = None,
1730
- outputs: list[Feature] = None,
1731
- feature_vector: str = None,
1732
- feature_weights: list = None,
1749
+ inputs: Optional[list[Feature]] = None,
1750
+ outputs: Optional[list[Feature]] = None,
1751
+ feature_vector: Optional[str] = None,
1752
+ feature_weights: Optional[list] = None,
1733
1753
  training_set=None,
1734
1754
  label_column=None,
1735
1755
  extra_data=None,
1736
1756
  **kwargs,
1737
- ):
1757
+ ) -> ModelArtifact:
1738
1758
  """Log a model artifact and optionally upload it to datastore
1739
1759
 
1740
1760
  If the model already exists with the same key and tag, it will be overwritten.
@@ -1930,7 +1950,6 @@ class MlrunProject(ModelObj):
1930
1950
  application_class: typing.Union[
1931
1951
  str,
1932
1952
  mm_app.ModelMonitoringApplicationBase,
1933
- mm_app.ModelMonitoringApplicationBaseV2,
1934
1953
  ] = None,
1935
1954
  name: str = None,
1936
1955
  image: str = None,
@@ -1998,7 +2017,6 @@ class MlrunProject(ModelObj):
1998
2017
  application_class: typing.Union[
1999
2018
  str,
2000
2019
  mm_app.ModelMonitoringApplicationBase,
2001
- mm_app.ModelMonitoringApplicationBaseV2,
2002
2020
  ] = None,
2003
2021
  name: str = None,
2004
2022
  image: str = None,
@@ -2056,7 +2074,6 @@ class MlrunProject(ModelObj):
2056
2074
  application_class: typing.Union[
2057
2075
  str,
2058
2076
  mm_app.ModelMonitoringApplicationBase,
2059
- mm_app.ModelMonitoringApplicationBaseV2,
2060
2077
  None,
2061
2078
  ] = None,
2062
2079
  name: typing.Optional[str] = None,
@@ -2145,7 +2162,8 @@ class MlrunProject(ModelObj):
2145
2162
 
2146
2163
  :param default_controller_image: Deprecated.
2147
2164
  :param base_period: The time period in minutes in which the model monitoring controller
2148
- function is triggered. By default, the base period is 10 minutes.
2165
+ function is triggered. By default, the base period is 10 minutes
2166
+ (which is also the minimum value for production environments).
2149
2167
  :param image: The image of the model monitoring controller, writer, monitoring
2150
2168
  stream & histogram data drift functions, which are real time nuclio
2151
2169
  functions. By default, the image is mlrun/mlrun.
@@ -2164,6 +2182,12 @@ class MlrunProject(ModelObj):
2164
2182
  FutureWarning,
2165
2183
  )
2166
2184
  image = default_controller_image
2185
+ if base_period < 10:
2186
+ logger.warn(
2187
+ "enable_model_monitoring: 'base_period' < 10 minutes is not supported in production environments",
2188
+ project=self.name,
2189
+ )
2190
+
2167
2191
  db = mlrun.db.get_run_db(secrets=self._secrets)
2168
2192
  db.enable_model_monitoring(
2169
2193
  project=self.name,
@@ -2310,31 +2334,51 @@ class MlrunProject(ModelObj):
2310
2334
  requirements: typing.Union[str, list[str]] = None,
2311
2335
  requirements_file: str = "",
2312
2336
  ) -> mlrun.runtimes.BaseRuntime:
2313
- """update or add a function object to the project
2337
+ """
2338
+ | Update or add a function object to the project.
2339
+ | Function can be provided as an object (func) or a .py/.ipynb/.yaml URL.
2314
2340
 
2315
- function can be provided as an object (func) or a .py/.ipynb/.yaml url
2316
- support url prefixes::
2341
+ | Creating a function from a single file is done by specifying ``func`` and disabling ``with_repo``.
2342
+ | Creating a function with project source (specify ``with_repo=True``):
2343
+ | 1. Specify a relative ``func`` path.
2344
+ | 2. Specify a module ``handler`` (e.g. ``handler=package.package.func``) without ``func``.
2345
+ | Creating a function with non project source is done by specifying a module ``handler`` and on the
2346
+ returned function set the source with ``function.with_source_archive(<source>)``.
2317
2347
 
2318
- object (s3://, v3io://, ..)
2319
- MLRun DB e.g. db://project/func:ver
2320
- functions hub/market: e.g. hub://auto-trainer:master
2348
+ Support URL prefixes:
2321
2349
 
2322
- examples::
2350
+ | Object (s3://, v3io://, ..)
2351
+ | MLRun DB e.g. db://project/func:ver
2352
+ | Functions hub/market: e.g. hub://auto-trainer:master
2353
+
2354
+ Examples::
2323
2355
 
2324
2356
  proj.set_function(func_object)
2325
- proj.set_function(
2326
- "./src/mycode.py", "ingest", image="myrepo/ing:latest", with_repo=True
2327
- )
2328
2357
  proj.set_function("http://.../mynb.ipynb", "train")
2329
2358
  proj.set_function("./func.yaml")
2330
2359
  proj.set_function("hub://get_toy_data", "getdata")
2331
2360
 
2332
- # set function requirements
2361
+ # Create a function from a single file
2362
+ proj.set_function("./src/mycode.py", "ingest")
2333
2363
 
2334
- # by providing a list of packages
2364
+ # Creating a function with project source
2365
+ proj.set_function(
2366
+ "./src/mycode.py", "ingest", image="myrepo/ing:latest", with_repo=True
2367
+ )
2368
+ proj.set_function("ingest", handler="package.package.func", with_repo=True)
2369
+
2370
+ # Creating a function with non project source
2371
+ func = proj.set_function(
2372
+ "ingest", handler="package.package.func", with_repo=False
2373
+ )
2374
+ func.with_source_archive("git://github.com/mlrun/something.git")
2375
+
2376
+ # Set function requirements
2377
+
2378
+ # By providing a list of packages
2335
2379
  proj.set_function("my.py", requirements=["requests", "pandas"])
2336
2380
 
2337
- # by providing a path to a pip requirements file
2381
+ # By providing a path to a pip requirements file
2338
2382
  proj.set_function("my.py", requirements="requirements.txt")
2339
2383
 
2340
2384
  :param func: Function object or spec/code url, None refers to current Notebook
@@ -2354,7 +2398,7 @@ class MlrunProject(ModelObj):
2354
2398
  :param requirements: A list of python packages
2355
2399
  :param requirements_file: Path to a python requirements file
2356
2400
 
2357
- :returns: function object
2401
+ :returns: :py:class:`~mlrun.runtimes.BaseRuntime`
2358
2402
  """
2359
2403
  (
2360
2404
  resolved_function_name,
@@ -2388,10 +2432,14 @@ class MlrunProject(ModelObj):
2388
2432
  requirements: typing.Union[str, list[str]] = None,
2389
2433
  requirements_file: str = "",
2390
2434
  ) -> tuple[str, str, mlrun.runtimes.BaseRuntime, dict]:
2391
- if func is None and not _has_module(handler, kind):
2435
+ if (
2436
+ func is None
2437
+ and not _has_module(handler, kind)
2438
+ and mlrun.runtimes.RuntimeKinds.supports_from_notebook(kind)
2439
+ ):
2392
2440
  # if function path is not provided and it is not a module (no ".")
2393
2441
  # use the current notebook as default
2394
- if is_ipython:
2442
+ if is_jupyter:
2395
2443
  from IPython import get_ipython
2396
2444
 
2397
2445
  kernel = get_ipython()
@@ -2782,47 +2830,104 @@ class MlrunProject(ModelObj):
2782
2830
  secrets=secrets or {},
2783
2831
  )
2784
2832
 
2785
- def sync_functions(self, names: list = None, always=True, save=False):
2786
- """reload function objects from specs and files"""
2833
+ def sync_functions(
2834
+ self,
2835
+ names: list = None,
2836
+ always: bool = True,
2837
+ save: bool = False,
2838
+ silent: bool = False,
2839
+ ):
2840
+ """
2841
+ Reload function objects from specs and files.
2842
+ The function objects are synced against the definitions spec in `self.spec._function_definitions`.
2843
+ Referenced files/URLs in the function spec will be reloaded.
2844
+ Function definitions are parsed by the following precedence:
2845
+
2846
+ 1. Contains runtime spec.
2847
+ 2. Contains module in the project's context.
2848
+ 3. Contains path to function definition (yaml, DB, Hub).
2849
+ 4. Contains path to .ipynb or .py files.
2850
+ 5. Contains a Nuclio/Serving function image / an 'Application' kind definition.
2851
+
2852
+ If function definition is already an object, some project metadata updates will apply however,
2853
+ it will not be reloaded.
2854
+
2855
+ :param names: Names of functions to reload, defaults to `self.spec._function_definitions.keys()`.
2856
+ :param always: Force reloading the functions.
2857
+ :param save: Whether to save the loaded functions or not.
2858
+ :param silent: Whether to raise an exception when a function fails to load.
2859
+
2860
+ :returns: Dictionary of function objects
2861
+ """
2787
2862
  if self._initialized and not always:
2788
2863
  return self.spec._function_objects
2789
2864
 
2790
- funcs = self.spec._function_objects
2865
+ functions = self.spec._function_objects
2791
2866
  if not names:
2792
2867
  names = self.spec._function_definitions.keys()
2793
- funcs = {}
2868
+ functions = {}
2869
+
2794
2870
  origin = mlrun.runtimes.utils.add_code_metadata(self.spec.context)
2795
2871
  for name in names:
2796
- f = self.spec._function_definitions.get(name)
2797
- if not f:
2798
- raise ValueError(f"function named {name} not found")
2872
+ function_definition = self.spec._function_definitions.get(name)
2873
+ if not function_definition:
2874
+ if silent:
2875
+ logger.warn(
2876
+ "Function definition was not found, skipping reload", name=name
2877
+ )
2878
+ continue
2879
+
2880
+ raise ValueError(f"Function named {name} not found")
2881
+
2882
+ function_object = self.spec._function_objects.get(name, None)
2883
+ is_base_runtime = isinstance(
2884
+ function_object, mlrun.runtimes.base.BaseRuntime
2885
+ )
2799
2886
  # If this function is already available locally, don't recreate it unless always=True
2800
- if (
2801
- isinstance(
2802
- self.spec._function_objects.get(name, None),
2803
- mlrun.runtimes.base.BaseRuntime,
2804
- )
2805
- and not always
2806
- ):
2807
- funcs[name] = self.spec._function_objects[name]
2887
+ if is_base_runtime and not always:
2888
+ functions[name] = function_object
2808
2889
  continue
2809
- if hasattr(f, "to_dict"):
2810
- name, func = _init_function_from_obj(f, self, name)
2811
- else:
2812
- if not isinstance(f, dict):
2813
- raise ValueError("function must be an object or dict")
2890
+
2891
+ # Reload the function
2892
+ if hasattr(function_definition, "to_dict"):
2893
+ name, func = _init_function_from_obj(function_definition, self, name)
2894
+ elif isinstance(function_definition, dict):
2814
2895
  try:
2815
- name, func = _init_function_from_dict(f, self, name)
2896
+ name, func = _init_function_from_dict(
2897
+ function_definition, self, name
2898
+ )
2816
2899
  except FileNotFoundError as exc:
2817
- raise mlrun.errors.MLRunMissingDependencyError(
2818
- f"File {exc.filename} not found while syncing project functions"
2819
- ) from exc
2900
+ message = f"File {exc.filename} not found while syncing project functions."
2901
+ if silent:
2902
+ message += " Skipping function reload"
2903
+ logger.warn(message, name=name)
2904
+ continue
2905
+
2906
+ raise mlrun.errors.MLRunMissingDependencyError(message) from exc
2907
+
2908
+ except Exception as exc:
2909
+ if silent:
2910
+ logger.warn(
2911
+ "Failed to instantiate function",
2912
+ name=name,
2913
+ error=mlrun.utils.err_to_str(exc),
2914
+ )
2915
+ continue
2916
+ raise exc
2917
+ else:
2918
+ message = f"Function {name} must be an object or dict."
2919
+ if silent:
2920
+ message += " Skipping function reload"
2921
+ logger.warn(message, name=name)
2922
+ continue
2923
+ raise ValueError(message)
2924
+
2820
2925
  func.spec.build.code_origin = origin
2821
- funcs[name] = func
2926
+ functions[name] = func
2822
2927
  if save:
2823
2928
  func.save(versioned=False)
2824
2929
 
2825
- self.spec._function_objects = funcs
2930
+ self.spec._function_objects = functions
2826
2931
  self._initialized = True
2827
2932
  return self.spec._function_objects
2828
2933
 
@@ -2967,6 +3072,7 @@ class MlrunProject(ModelObj):
2967
3072
  source: str = None,
2968
3073
  cleanup_ttl: int = None,
2969
3074
  notifications: list[mlrun.model.Notification] = None,
3075
+ workflow_runner_node_selector: typing.Optional[dict[str, str]] = None,
2970
3076
  ) -> _PipelineRunStatus:
2971
3077
  """Run a workflow using kubeflow pipelines
2972
3078
 
@@ -2995,15 +3101,20 @@ class MlrunProject(ModelObj):
2995
3101
 
2996
3102
  * Remote URL which is loaded dynamically to the workflow runner.
2997
3103
  * A path to the project's context on the workflow runner's image.
2998
- Path can be absolute or relative to `project.spec.build.source_code_target_dir` if defined
2999
- (enriched when building a project image with source, see `MlrunProject.build_image`).
3000
- For other engines the source is used to validate that the code is up-to-date.
3104
+ Path can be absolute or relative to `project.spec.build.source_code_target_dir` if defined
3105
+ (enriched when building a project image with source, see `MlrunProject.build_image`).
3106
+ For other engines the source is used to validate that the code is up-to-date.
3107
+
3001
3108
  :param cleanup_ttl:
3002
3109
  Pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
3003
3110
  workflow and all its resources are deleted)
3004
3111
  :param notifications:
3005
3112
  List of notifications to send for workflow completion
3006
-
3113
+ :param workflow_runner_node_selector:
3114
+ Defines the node selector for the workflow runner pod when using a remote engine.
3115
+ This allows you to control and specify where the workflow runner pod will be scheduled.
3116
+ This setting is only relevant when the engine is set to 'remote' or for scheduled workflows,
3117
+ and it will be ignored if the workflow is not run on a remote engine.
3007
3118
  :returns: ~py:class:`~mlrun.projects.pipelines._PipelineRunStatus` instance
3008
3119
  """
3009
3120
 
@@ -3021,11 +3132,11 @@ class MlrunProject(ModelObj):
3021
3132
  "Remote repo is not defined, use .create_remote() + push()"
3022
3133
  )
3023
3134
 
3024
- if engine not in ["remote"]:
3025
- # for remote runs we don't require the functions to be synced as they can be loaded dynamically during run
3026
- self.sync_functions(always=sync)
3135
+ if engine not in ["remote"] and not schedule:
3136
+ # For remote/scheduled runs there is no need to sync functions as they can be loaded dynamically during run
3137
+ self.sync_functions(always=sync, silent=True)
3027
3138
  if not self.spec._function_objects:
3028
- raise ValueError(
3139
+ logger.warn(
3029
3140
  "There are no functions in the project."
3030
3141
  " Make sure you've set your functions with project.set_function()."
3031
3142
  )
@@ -3069,6 +3180,16 @@ class MlrunProject(ModelObj):
3069
3180
  )
3070
3181
  inner_engine = get_workflow_engine(engine_kind, local).engine
3071
3182
  workflow_spec.engine = inner_engine or workflow_engine.engine
3183
+ if workflow_runner_node_selector:
3184
+ if workflow_engine.engine == "remote":
3185
+ workflow_spec.workflow_runner_node_selector = (
3186
+ workflow_runner_node_selector
3187
+ )
3188
+ else:
3189
+ logger.warn(
3190
+ "'workflow_runner_node_selector' applies only to remote engines"
3191
+ " and is ignored for non-remote runs."
3192
+ )
3072
3193
 
3073
3194
  run = workflow_engine.run(
3074
3195
  self,
@@ -3205,35 +3326,41 @@ class MlrunProject(ModelObj):
3205
3326
  endpoint_store_connection: Optional[str] = None,
3206
3327
  stream_path: Optional[str] = None,
3207
3328
  tsdb_connection: Optional[str] = None,
3329
+ replace_creds: bool = False,
3208
3330
  ):
3209
3331
  """
3210
3332
  Set the credentials that will be used by the project's model monitoring
3211
3333
  infrastructure functions. Important to note that you have to set the credentials before deploying any
3212
3334
  model monitoring or serving function.
3213
3335
 
3214
- :param access_key: Model Monitoring access key for managing user permissions.
3215
- :param endpoint_store_connection: Endpoint store connection string. By default, None.
3216
- Options:
3217
- 1. None, will be set from the system configuration.
3218
- 2. v3io - for v3io endpoint store,
3219
- pass `v3io` and the system will generate the exact path.
3220
- 3. MySQL/SQLite - for SQL endpoint store, please provide full
3221
- connection string, for example
3222
- mysql+pymysql://<username>:<password>@<host>:<port>/<db_name>
3223
- :param stream_path: Path to the model monitoring stream. By default, None.
3224
- Options:
3225
- 1. None, will be set from the system configuration.
3226
- 2. v3io - for v3io stream,
3227
- pass `v3io` and the system will generate the exact path.
3228
- 3. Kafka - for Kafka stream, please provide full connection string without
3229
- custom topic, for example kafka://<some_kafka_broker>:<port>.
3336
+ :param access_key: Model monitoring access key for managing user permissions.
3337
+ :param endpoint_store_connection: Endpoint store connection string. By default, None. Options:
3338
+
3339
+ * None - will be set from the system configuration.
3340
+ * v3io - for v3io endpoint store, pass `v3io` and the system will generate the
3341
+ exact path.
3342
+ * MySQL/SQLite - for SQL endpoint store, provide the full connection string,
3343
+ for example: mysql+pymysql://<username>:<password>@<host>:<port>/<db_name>
3344
+ :param stream_path: Path to the model monitoring stream. By default, None. Options:
3345
+
3346
+ * None - will be set from the system configuration.
3347
+ * v3io - for v3io stream, pass `v3io` and the system will generate the exact
3348
+ path.
3349
+ * Kafka - for Kafka stream, provide the full connection string without custom
3350
+ topic, for example kafka://<some_kafka_broker>:<port>.
3230
3351
  :param tsdb_connection: Connection string to the time series database. By default, None.
3231
3352
  Options:
3232
- 1. None, will be set from the system configuration.
3233
- 2. v3io - for v3io stream,
3234
- pass `v3io` and the system will generate the exact path.
3235
- 3. TDEngine - for TDEngine tsdb, please provide full websocket connection URL,
3236
- for example taosws://<username>:<password>@<host>:<port>.
3353
+
3354
+ * None - will be set from the system configuration.
3355
+ * v3io - for v3io stream, pass `v3io` and the system will generate the exact
3356
+ path.
3357
+ * TDEngine - for TDEngine tsdb, provide the full websocket connection URL,
3358
+ for example taosws://<username>:<password>@<host>:<port>.
3359
+ :param replace_creds: If True, will override the existing credentials.
3360
+ Please keep in mind that if you already enabled model monitoring on
3361
+ your project this action can cause data loose and will require redeploying
3362
+ all model monitoring functions & model monitoring infra
3363
+ & tracked model server.
3237
3364
  """
3238
3365
  db = mlrun.db.get_run_db(secrets=self._secrets)
3239
3366
  db.set_model_monitoring_credentials(
@@ -3244,7 +3371,17 @@ class MlrunProject(ModelObj):
3244
3371
  "stream_path": stream_path,
3245
3372
  "tsdb_connection": tsdb_connection,
3246
3373
  },
3374
+ replace_creds=replace_creds,
3247
3375
  )
3376
+ if replace_creds:
3377
+ logger.info(
3378
+ "Model monitoring credentials were set successfully. "
3379
+ "Please keep in mind that if you already had model monitoring functions "
3380
+ "/ model monitoring infra / tracked model server "
3381
+ "deployed on your project, you will need to redeploy them."
3382
+ "For redeploying the model monitoring infra, please use `enable_model_monitoring` API "
3383
+ "and set `rebuild_images=True`"
3384
+ )
3248
3385
 
3249
3386
  def run_function(
3250
3387
  self,
@@ -3325,7 +3462,8 @@ class MlrunProject(ModelObj):
3325
3462
  * A dictionary of configurations to use when logging. Further info per object type and
3326
3463
  artifact type can be given there. The artifact key must appear in the dictionary as
3327
3464
  "key": "the_key".
3328
- :param builder_env: env vars dict for source archive config/credentials e.g. builder_env={"GIT_TOKEN": token}
3465
+ :param builder_env: env vars dict for source archive config/credentials e.g. builder_env={"GIT_TOKEN":
3466
+ token}
3329
3467
  :param reset_on_run: When True, function python modules would reload prior to code execution.
3330
3468
  This ensures latest code changes are executed. This argument must be used in
3331
3469
  conjunction with the local=True argument.
@@ -3765,7 +3903,7 @@ class MlrunProject(ModelObj):
3765
3903
 
3766
3904
 
3767
3905
  :param name: Return only functions with a specific name.
3768
- :param tag: Return function versions with specific tags.
3906
+ :param tag: Return function versions with specific tags. To return only tagged functions, set tag to ``"*"``.
3769
3907
  :param labels: Return functions that have specific labels assigned to them.
3770
3908
  :returns: List of function objects.
3771
3909
  """
@@ -4035,7 +4173,7 @@ class MlrunProject(ModelObj):
4035
4173
  mlrun.db.get_run_db().delete_api_gateway(name=name, project=self.name)
4036
4174
 
4037
4175
  def store_alert_config(
4038
- self, alert_data: AlertConfig, alert_name=None
4176
+ self, alert_data: AlertConfig, alert_name: typing.Optional[str] = None
4039
4177
  ) -> AlertConfig:
4040
4178
  """
4041
4179
  Create/modify an alert.
@@ -4044,9 +4182,17 @@ class MlrunProject(ModelObj):
4044
4182
  :param alert_name: The name of the alert.
4045
4183
  :return: the created/modified alert.
4046
4184
  """
4185
+ if not alert_data:
4186
+ raise mlrun.errors.MLRunInvalidArgumentError("Alert data must be provided")
4187
+
4047
4188
  db = mlrun.db.get_run_db(secrets=self._secrets)
4048
- if alert_name is None:
4049
- alert_name = alert_data.name
4189
+ alert_name = alert_name or alert_data.name
4190
+ if alert_data.project is not None and alert_data.project != self.metadata.name:
4191
+ logger.warn(
4192
+ "Project in alert does not match project in operation",
4193
+ project=alert_data.project,
4194
+ )
4195
+ alert_data.project = self.metadata.name
4050
4196
  return db.store_alert_config(alert_name, alert_data, project=self.metadata.name)
4051
4197
 
4052
4198
  def get_alert_config(self, alert_name: str) -> AlertConfig:
@@ -4244,6 +4390,7 @@ class MlrunProject(ModelObj):
4244
4390
  kind=producer_dict.get("kind", ""),
4245
4391
  project=producer_project,
4246
4392
  tag=producer_tag,
4393
+ owner=producer_dict.get("owner", ""),
4247
4394
  ), True
4248
4395
 
4249
4396
  # do not retain the artifact's producer, replace it with the project as the producer
@@ -4253,6 +4400,7 @@ class MlrunProject(ModelObj):
4253
4400
  name=self.metadata.name,
4254
4401
  project=self.metadata.name,
4255
4402
  tag=project_producer_tag,
4403
+ owner=self._resolve_artifact_owner(),
4256
4404
  ), False
4257
4405
 
4258
4406
  def _resolve_existing_artifact(
@@ -4292,6 +4440,9 @@ class MlrunProject(ModelObj):
4292
4440
  def _get_project_tag(self):
4293
4441
  return self._get_hexsha() or str(uuid.uuid4())
4294
4442
 
4443
+ def _resolve_artifact_owner(self):
4444
+ return os.getenv("V3IO_USERNAME") or self.spec.owner
4445
+
4295
4446
 
4296
4447
  def _set_as_current_default_project(project: MlrunProject):
4297
4448
  mlrun.mlconf.default_project = project.metadata.name
@@ -4343,18 +4494,17 @@ def _init_function_from_dict(
4343
4494
  )
4344
4495
 
4345
4496
  elif url.endswith(".py"):
4346
- # when load_source_on_run is used we allow not providing image as code will be loaded pre-run. ML-4994
4347
- if (
4348
- not image
4349
- and not project.default_image
4350
- and kind != "local"
4351
- and not project.spec.load_source_on_run
4352
- ):
4353
- raise ValueError(
4354
- "image must be provided with py code files which do not "
4355
- "run on 'local' engine kind"
4356
- )
4357
4497
  if in_context and with_repo:
4498
+ # when load_source_on_run is used we allow not providing image as code will be loaded pre-run. ML-4994
4499
+ if (
4500
+ not image
4501
+ and not project.default_image
4502
+ and kind != "local"
4503
+ and not project.spec.load_source_on_run
4504
+ ):
4505
+ raise ValueError(
4506
+ "image must be provided with py code files which do not run on 'local' engine kind"
4507
+ )
4358
4508
  func = new_function(
4359
4509
  name,
4360
4510
  command=relative_url,
@@ -4376,7 +4526,6 @@ def _init_function_from_dict(
4376
4526
  elif kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes():
4377
4527
  func = new_function(
4378
4528
  name,
4379
- command=relative_url,
4380
4529
  image=image,
4381
4530
  kind=kind,
4382
4531
  handler=handler,
@@ -4430,9 +4579,17 @@ def _init_function_from_obj(
4430
4579
  def _has_module(handler, kind):
4431
4580
  if not handler:
4432
4581
  return False
4433
- return (
4434
- kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes() and ":" in handler
4435
- ) or "." in handler
4582
+
4583
+ if (
4584
+ kind in mlrun.runtimes.RuntimeKinds.pure_nuclio_deployed_runtimes()
4585
+ and ":" in handler
4586
+ ):
4587
+ return True
4588
+
4589
+ if "." in handler:
4590
+ return True
4591
+
4592
+ return False
4436
4593
 
4437
4594
 
4438
4595
  def _is_imported_artifact(artifact):