mlrun 1.10.0rc16__py3-none-any.whl → 1.10.0rc42__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 (98) hide show
  1. mlrun/__init__.py +22 -2
  2. mlrun/artifacts/document.py +6 -1
  3. mlrun/artifacts/llm_prompt.py +21 -15
  4. mlrun/artifacts/model.py +3 -3
  5. mlrun/common/constants.py +9 -0
  6. mlrun/common/formatters/artifact.py +1 -0
  7. mlrun/common/model_monitoring/helpers.py +86 -0
  8. mlrun/common/schemas/__init__.py +2 -0
  9. mlrun/common/schemas/auth.py +2 -0
  10. mlrun/common/schemas/function.py +10 -0
  11. mlrun/common/schemas/hub.py +30 -18
  12. mlrun/common/schemas/model_monitoring/__init__.py +2 -0
  13. mlrun/common/schemas/model_monitoring/constants.py +30 -6
  14. mlrun/common/schemas/model_monitoring/functions.py +13 -4
  15. mlrun/common/schemas/model_monitoring/model_endpoints.py +11 -0
  16. mlrun/common/schemas/pipeline.py +1 -1
  17. mlrun/common/schemas/serving.py +3 -0
  18. mlrun/common/schemas/workflow.py +1 -0
  19. mlrun/common/secrets.py +22 -1
  20. mlrun/config.py +32 -10
  21. mlrun/datastore/__init__.py +11 -3
  22. mlrun/datastore/azure_blob.py +162 -47
  23. mlrun/datastore/datastore.py +9 -4
  24. mlrun/datastore/datastore_profile.py +61 -5
  25. mlrun/datastore/model_provider/huggingface_provider.py +363 -0
  26. mlrun/datastore/model_provider/mock_model_provider.py +87 -0
  27. mlrun/datastore/model_provider/model_provider.py +211 -74
  28. mlrun/datastore/model_provider/openai_provider.py +243 -71
  29. mlrun/datastore/s3.py +24 -2
  30. mlrun/datastore/storeytargets.py +2 -3
  31. mlrun/datastore/utils.py +15 -3
  32. mlrun/db/base.py +27 -19
  33. mlrun/db/httpdb.py +57 -48
  34. mlrun/db/nopdb.py +25 -10
  35. mlrun/execution.py +55 -13
  36. mlrun/hub/__init__.py +15 -0
  37. mlrun/hub/module.py +181 -0
  38. mlrun/k8s_utils.py +105 -16
  39. mlrun/launcher/base.py +13 -6
  40. mlrun/launcher/local.py +2 -0
  41. mlrun/model.py +9 -3
  42. mlrun/model_monitoring/api.py +66 -27
  43. mlrun/model_monitoring/applications/__init__.py +1 -1
  44. mlrun/model_monitoring/applications/base.py +372 -136
  45. mlrun/model_monitoring/applications/context.py +2 -4
  46. mlrun/model_monitoring/applications/results.py +4 -7
  47. mlrun/model_monitoring/controller.py +239 -101
  48. mlrun/model_monitoring/db/_schedules.py +36 -13
  49. mlrun/model_monitoring/db/_stats.py +4 -3
  50. mlrun/model_monitoring/db/tsdb/base.py +29 -9
  51. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
  52. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +154 -50
  53. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
  54. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
  55. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +245 -51
  56. mlrun/model_monitoring/helpers.py +28 -5
  57. mlrun/model_monitoring/stream_processing.py +45 -14
  58. mlrun/model_monitoring/writer.py +220 -1
  59. mlrun/platforms/__init__.py +3 -2
  60. mlrun/platforms/iguazio.py +7 -3
  61. mlrun/projects/operations.py +6 -1
  62. mlrun/projects/pipelines.py +2 -2
  63. mlrun/projects/project.py +128 -45
  64. mlrun/run.py +94 -17
  65. mlrun/runtimes/__init__.py +18 -0
  66. mlrun/runtimes/base.py +14 -6
  67. mlrun/runtimes/daskjob.py +1 -0
  68. mlrun/runtimes/local.py +5 -2
  69. mlrun/runtimes/mounts.py +20 -2
  70. mlrun/runtimes/nuclio/__init__.py +1 -0
  71. mlrun/runtimes/nuclio/application/application.py +147 -17
  72. mlrun/runtimes/nuclio/function.py +70 -27
  73. mlrun/runtimes/nuclio/serving.py +85 -4
  74. mlrun/runtimes/pod.py +213 -21
  75. mlrun/runtimes/utils.py +49 -9
  76. mlrun/secrets.py +54 -13
  77. mlrun/serving/remote.py +79 -6
  78. mlrun/serving/routers.py +23 -41
  79. mlrun/serving/server.py +211 -40
  80. mlrun/serving/states.py +536 -156
  81. mlrun/serving/steps.py +62 -0
  82. mlrun/serving/system_steps.py +136 -81
  83. mlrun/serving/v2_serving.py +9 -10
  84. mlrun/utils/helpers.py +212 -82
  85. mlrun/utils/logger.py +3 -1
  86. mlrun/utils/notifications/notification/base.py +18 -0
  87. mlrun/utils/notifications/notification/git.py +2 -4
  88. mlrun/utils/notifications/notification/slack.py +2 -4
  89. mlrun/utils/notifications/notification/webhook.py +2 -5
  90. mlrun/utils/notifications/notification_pusher.py +1 -1
  91. mlrun/utils/version/version.json +2 -2
  92. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/METADATA +44 -45
  93. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/RECORD +97 -92
  94. mlrun/api/schemas/__init__.py +0 -259
  95. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/WHEEL +0 -0
  96. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/entry_points.txt +0 -0
  97. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/licenses/LICENSE +0 -0
  98. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py CHANGED
@@ -45,6 +45,7 @@ import mlrun.common.runtimes.constants
45
45
  import mlrun.common.schemas.alert
46
46
  import mlrun.common.schemas.artifact
47
47
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
48
+ import mlrun.common.secrets
48
49
  import mlrun.datastore.datastore_profile
49
50
  import mlrun.db
50
51
  import mlrun.errors
@@ -1888,7 +1889,7 @@ class MlrunProject(ModelObj):
1888
1889
  prompt_path: Optional[str] = None,
1889
1890
  prompt_legend: Optional[dict] = None,
1890
1891
  model_artifact: Union[ModelArtifact, str] = None,
1891
- model_configuration: Optional[dict] = None,
1892
+ invocation_config: Optional[dict] = None,
1892
1893
  description: Optional[str] = None,
1893
1894
  target_path: Optional[str] = None,
1894
1895
  artifact_path: Optional[str] = None,
@@ -1908,13 +1909,51 @@ class MlrunProject(ModelObj):
1908
1909
 
1909
1910
  Examples::
1910
1911
 
1912
+ # Log directly with an inline prompt template
1913
+ project.log_llm_prompt(
1914
+ key="customer_support_prompt",
1915
+ prompt_template=[
1916
+ {
1917
+ "role": "system",
1918
+ "content": "You are a helpful customer support assistant.",
1919
+ },
1920
+ {
1921
+ "role": "user",
1922
+ "content": "The customer reports: {issue_description}",
1923
+ },
1924
+ ],
1925
+ prompt_legend={
1926
+ "issue_description": {
1927
+ "field": "user_issue",
1928
+ "description": "Detailed description of the customer's issue",
1929
+ },
1930
+ "solution": {
1931
+ "field": "proposed_solution",
1932
+ "description": "Suggested fix for the customer's issue",
1933
+ },
1934
+ },
1935
+ model_artifact=model,
1936
+ invocation_config={"temperature": 0.5, "max_tokens": 200},
1937
+ description="Prompt for handling customer support queries",
1938
+ tag="support-v1",
1939
+ labels={"domain": "support"},
1940
+ )
1941
+
1911
1942
  # Log a prompt from file
1912
1943
  project.log_llm_prompt(
1913
- key="qa-prompt",
1914
- prompt_path="prompts/qa_template.txt",
1915
- prompt_legend={"question": "user_question"},
1944
+ key="qa_prompt",
1945
+ prompt_path="prompts/template.json",
1946
+ prompt_legend={
1947
+ "question": {
1948
+ "field": "user_question",
1949
+ "description": "The actual question asked by the user",
1950
+ }
1951
+ },
1916
1952
  model_artifact=model,
1953
+ invocation_config={"temperature": 0.7, "max_tokens": 256},
1954
+ description="Q&A prompt template with user-provided question",
1917
1955
  tag="v2",
1956
+ labels={"task": "qa", "stage": "experiment"},
1918
1957
  )
1919
1958
 
1920
1959
  :param key: Unique key for the prompt artifact.
@@ -1923,18 +1962,23 @@ class MlrunProject(ModelObj):
1923
1962
  "role": "user", "content": "I need your help with {profession}"]. only "role" and "content" keys allow in any
1924
1963
  str format (upper/lower case), keys will be modified to lower case.
1925
1964
  Cannot be used with `prompt_path`.
1926
- :param prompt_path: Path to a file containing the prompt. Mutually exclusive with `prompt_string`.
1965
+ :param prompt_path: Path to a JSON file containing the prompt template.
1966
+ Cannot be used together with `prompt_template`.
1967
+ The file should define a list of dictionaries in the same format
1968
+ supported by `prompt_template`.
1927
1969
  :param prompt_legend: A dictionary where each key is a placeholder in the prompt (e.g., ``{user_name}``)
1928
1970
  and the value is a dictionary holding two keys, "field", "description". "field" points to the field in
1929
1971
  the event where the value of the place-holder inside the event, if None or not exist will be replaced
1930
1972
  with the place-holder name. "description" will point to explanation of what that placeholder represents.
1931
1973
  Useful for documenting and clarifying dynamic parts of the prompt.
1932
1974
  :param model_artifact: Reference to the parent model (either `ModelArtifact` or model URI string).
1933
- :param model_configuration: Configuration dictionary for model generation parameters
1975
+ :param invocation_config: Configuration dictionary for model generation parameters
1934
1976
  (e.g., temperature, max tokens).
1935
- :param description: Optional description of the prompt.
1936
- :param target_path: Optional local target path for saving prompt content.
1937
- :param artifact_path: Storage path for the logged artifact.
1977
+ :param description: Optional description of the prompt.
1978
+ :param target_path: Absolute target path (instead of using artifact_path + local_path)
1979
+ :param artifact_path: Target artifact path (when not using the default)
1980
+ To define a subpath under the default location use:
1981
+ `artifact_path=context.artifact_subpath('data')`
1938
1982
  :param tag: Version tag for the artifact (e.g., "v1", "latest").
1939
1983
  :param labels: Labels to tag the artifact for filtering and organization.
1940
1984
  :param upload: Whether to upload the artifact to a remote datastore. Defaults to True.
@@ -1955,7 +1999,7 @@ class MlrunProject(ModelObj):
1955
1999
  prompt_path=prompt_path,
1956
2000
  prompt_legend=prompt_legend,
1957
2001
  model_artifact=model_artifact,
1958
- model_configuration=model_configuration,
2002
+ invocation_config=invocation_config,
1959
2003
  target_path=target_path,
1960
2004
  description=description,
1961
2005
  **kwargs,
@@ -2343,8 +2387,9 @@ class MlrunProject(ModelObj):
2343
2387
  handler: Optional[str] = None,
2344
2388
  with_repo: Optional[bool] = None,
2345
2389
  tag: Optional[str] = None,
2346
- requirements: Optional[typing.Union[str, list[str]]] = None,
2390
+ requirements: Optional[list[str]] = None,
2347
2391
  requirements_file: str = "",
2392
+ local_path: Optional[str] = None,
2348
2393
  **application_kwargs,
2349
2394
  ) -> mlrun.runtimes.RemoteRuntime:
2350
2395
  """
@@ -2359,7 +2404,8 @@ class MlrunProject(ModelObj):
2359
2404
  )
2360
2405
 
2361
2406
  :param func: Remote function object or spec/code URL. :code:`None` refers to the current
2362
- notebook.
2407
+ notebook. May also be a hub URL of a module of kind model-monitoring-app in the
2408
+ format: hub://[{source}/]{name}[:{tag}].
2363
2409
  :param name: Name of the function (under the project), can be specified with a tag to support
2364
2410
  versions (e.g. myfunc:v1).
2365
2411
  :param image: Docker image to be used, can also be specified in
@@ -2374,6 +2420,8 @@ class MlrunProject(ModelObj):
2374
2420
  :param application_class: Name or an Instance of a class that implements the monitoring application.
2375
2421
  :param application_kwargs: Additional keyword arguments to be passed to the
2376
2422
  monitoring application's constructor.
2423
+ :param local_path: Path to a local directory to save the downloaded monitoring-app code files in,
2424
+ in case 'func' is a hub URL (defaults to current working directory).
2377
2425
  :returns: The model monitoring remote function object.
2378
2426
  """
2379
2427
  (
@@ -2390,6 +2438,7 @@ class MlrunProject(ModelObj):
2390
2438
  tag,
2391
2439
  requirements,
2392
2440
  requirements_file,
2441
+ local_path,
2393
2442
  **application_kwargs,
2394
2443
  )
2395
2444
  # save to project spec
@@ -2468,8 +2517,9 @@ class MlrunProject(ModelObj):
2468
2517
  handler: typing.Optional[str] = None,
2469
2518
  with_repo: typing.Optional[bool] = None,
2470
2519
  tag: typing.Optional[str] = None,
2471
- requirements: typing.Union[str, list[str], None] = None,
2520
+ requirements: typing.Union[list[str], None] = None,
2472
2521
  requirements_file: str = "",
2522
+ local_path: typing.Optional[str] = None,
2473
2523
  **application_kwargs,
2474
2524
  ) -> tuple[str, mlrun.runtimes.RemoteRuntime, dict]:
2475
2525
  import mlrun.model_monitoring.api
@@ -2486,6 +2536,7 @@ class MlrunProject(ModelObj):
2486
2536
  tag=tag,
2487
2537
  requirements=requirements,
2488
2538
  requirements_file=requirements_file,
2539
+ local_path=local_path,
2489
2540
  **application_kwargs,
2490
2541
  )
2491
2542
  elif isinstance(func, str) and isinstance(handler, str):
@@ -2531,7 +2582,7 @@ class MlrunProject(ModelObj):
2531
2582
  *,
2532
2583
  deploy_histogram_data_drift_app: bool = True,
2533
2584
  wait_for_deployment: bool = False,
2534
- fetch_credentials_from_sys_config: bool = False,
2585
+ fetch_credentials_from_sys_config: bool = False, # deprecated
2535
2586
  ) -> None:
2536
2587
  """
2537
2588
  Deploy model monitoring application controller, writer and stream functions.
@@ -2566,14 +2617,20 @@ class MlrunProject(ModelObj):
2566
2617
  :param wait_for_deployment: If true, return only after the deployment is done on the backend.
2567
2618
  Otherwise, deploy the model monitoring infrastructure on the
2568
2619
  background, including the histogram data drift app if selected.
2569
- :param fetch_credentials_from_sys_config: If true, fetch the credentials from the system configuration.
2620
+ :param fetch_credentials_from_sys_config: Deprecated. If true, fetch the credentials from the project
2621
+ configuration.
2570
2622
  """
2623
+ if fetch_credentials_from_sys_config:
2624
+ warnings.warn(
2625
+ "`fetch_credentials_from_sys_config` is deprecated in 1.10.0 and will be removed in 1.12.0.",
2626
+ # TODO: Remove this in 1.12.0
2627
+ FutureWarning,
2628
+ )
2571
2629
  if base_period < 10:
2572
2630
  logger.warn(
2573
2631
  "enable_model_monitoring: 'base_period' < 10 minutes is not supported in production environments",
2574
2632
  project=self.name,
2575
2633
  )
2576
-
2577
2634
  db = mlrun.db.get_run_db(secrets=self._secrets)
2578
2635
  db.enable_model_monitoring(
2579
2636
  project=self.name,
@@ -2706,16 +2763,18 @@ class MlrunProject(ModelObj):
2706
2763
  | Creating a function with non project source is done by specifying a module ``handler`` and on the
2707
2764
  returned function set the source with ``function.with_source_archive(<source>)``.
2708
2765
 
2709
- Support URL prefixes:
2766
+ Supported URL prefixes:
2710
2767
 
2711
- | Object (s3://, v3io://, ..)
2712
- | MLRun DB e.g. db://project/func:ver
2713
- | Functions hub/market: e.g. hub://auto-trainer:master
2768
+ - Object: s3://, v3io://, etc.
2769
+ - MLRun DB: e.g db://project/func:ver
2770
+ - Function hub/market: e.g. hub://auto-trainer:master
2714
2771
 
2715
2772
  Examples::
2716
2773
 
2717
2774
  proj.set_function(func_object)
2718
- proj.set_function("http://.../mynb.ipynb", "train")
2775
+ proj.set_function(
2776
+ "http://.../mynb.ipynb", "train", kind="job", image="mlrun/mlrun"
2777
+ )
2719
2778
  proj.set_function("./func.yaml")
2720
2779
  proj.set_function("hub://get_toy_data", "getdata")
2721
2780
 
@@ -2742,18 +2801,6 @@ class MlrunProject(ModelObj):
2742
2801
  # By providing a path to a pip requirements file
2743
2802
  proj.set_function("my.py", requirements="requirements.txt")
2744
2803
 
2745
- One of the most important parameters is 'kind', used to specify the chosen runtime. The options are:
2746
- - local: execute a local python or shell script
2747
- - job: insert the code into a Kubernetes pod and execute it
2748
- - nuclio: insert the code into a real-time serverless nuclio function
2749
- - serving: insert code into orchestrated nuclio function(s) forming a DAG
2750
- - dask: run the specified python code / script as Dask Distributed job
2751
- - mpijob: run distributed Horovod jobs over the MPI job operator
2752
- - spark: run distributed Spark job using Spark Kubernetes Operator
2753
- - remote-spark: run distributed Spark job on remote Spark service
2754
- - databricks: run code on Databricks cluster (python scripts, Spark etc.)
2755
- - application: run a long living application (e.g. a web server, UI, etc.)
2756
-
2757
2804
  Learn more about :doc:`../../concepts/functions-overview`.
2758
2805
 
2759
2806
  :param func: Function object or spec/code url, None refers to current Notebook
@@ -2761,8 +2808,20 @@ class MlrunProject(ModelObj):
2761
2808
  Versions (e.g. myfunc:v1). If the `tag` parameter is provided, the tag in the name
2762
2809
  must match the tag parameter.
2763
2810
  Specifying a tag in the name will update the project's tagged function (myfunc:v1)
2764
- :param kind: Runtime kind e.g. job, nuclio, spark, dask, mpijob
2765
- Default: job
2811
+ :param kind: Default: job. One of
2812
+
2813
+ - local: execute a local python or shell script
2814
+ - job: insert the code into a Kubernetes pod and execute it
2815
+ - nuclio: insert the code into a real-time serverless nuclio function
2816
+ - serving: insert code into orchestrated nuclio function(s) forming a DAG
2817
+ - dask: run the specified python code / script as Dask Distributed job
2818
+ - mpijob: run distributed Horovod jobs over the MPI job operator
2819
+ - spark: run distributed Spark job using Spark Kubernetes Operator
2820
+ - remote-spark: run distributed Spark job on remote Spark service
2821
+ - databricks: run code on Databricks cluster (python scripts, Spark etc.)
2822
+ - application: run a long living application (e.g. a web server, UI, etc.)
2823
+ - handler: execute a python handler (used automatically in notebooks or for debug)
2824
+
2766
2825
  :param image: Docker image to be used, can also be specified in the function object/yaml
2767
2826
  :param handler: Default function handler to invoke (can only be set with .py/.ipynb files)
2768
2827
  :param with_repo: Add (clone) the current repo to the build source - use when the function code is in
@@ -3360,7 +3419,12 @@ class MlrunProject(ModelObj):
3360
3419
  self._initialized = True
3361
3420
  return self.spec._function_objects
3362
3421
 
3363
- def with_secrets(self, kind, source, prefix=""):
3422
+ def with_secrets(
3423
+ self,
3424
+ kind,
3425
+ source,
3426
+ prefix="",
3427
+ ):
3364
3428
  """register a secrets source (file, env or dict)
3365
3429
 
3366
3430
  read secrets from a source provider to be used in workflows, example::
@@ -3382,12 +3446,19 @@ class MlrunProject(ModelObj):
3382
3446
 
3383
3447
  This will enable access to all secrets in vault registered to the current project.
3384
3448
 
3385
- :param kind: secret type (file, inline, env, vault)
3449
+ :param kind: secret type (file, inline, env, vault, azure_vault)
3386
3450
  :param source: secret data or link (see example)
3387
3451
  :param prefix: add a prefix to the keys in this source
3388
3452
 
3389
3453
  :returns: project object
3390
3454
  """
3455
+ # Block using mlrun-auth-secrets.* via azure_vault's k8s_secret param (client-side only)
3456
+ if kind == "azure_vault" and isinstance(source, dict):
3457
+ candidate_secret_name = (source.get("k8s_secret") or "").strip()
3458
+ if candidate_secret_name:
3459
+ mlrun.common.secrets.validate_not_forbidden_secret(
3460
+ candidate_secret_name
3461
+ )
3391
3462
 
3392
3463
  if kind == "vault" and isinstance(source, list):
3393
3464
  source = {"project": self.metadata.name, "secrets": source}
@@ -3771,7 +3842,7 @@ class MlrunProject(ModelObj):
3771
3842
 
3772
3843
  import mlrun
3773
3844
  from mlrun.datastore.datastore_profile import (
3774
- DatastoreProfileKafkaSource,
3845
+ DatastoreProfileKafkaStream,
3775
3846
  DatastoreProfileTDEngine,
3776
3847
  )
3777
3848
 
@@ -3788,7 +3859,7 @@ class MlrunProject(ModelObj):
3788
3859
  project.register_datastore_profile(tsdb_profile)
3789
3860
 
3790
3861
  # Create and register stream profile
3791
- stream_profile = DatastoreProfileKafkaSource(
3862
+ stream_profile = DatastoreProfileKafkaStream(
3792
3863
  name="my-kafka",
3793
3864
  brokers=["<kafka-broker-ip-address>:9094"],
3794
3865
  topics=[], # Keep the topics list empty
@@ -3830,9 +3901,9 @@ class MlrunProject(ModelObj):
3830
3901
 
3831
3902
  .. code-block:: python
3832
3903
 
3833
- from mlrun.datastore.datastore_profile import DatastoreProfileKafkaSource
3904
+ from mlrun.datastore.datastore_profile import DatastoreProfileKafkaStream
3834
3905
 
3835
- stream_profile = DatastoreProfileKafkaSource(
3906
+ stream_profile = DatastoreProfileKafkaStream(
3836
3907
  name="confluent-kafka",
3837
3908
  brokers=["<server-domain-start>.confluent.cloud:9092"],
3838
3909
  topics=[],
@@ -3861,7 +3932,7 @@ class MlrunProject(ModelObj):
3861
3932
  The supported profiles are:
3862
3933
 
3863
3934
  * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileV3io`
3864
- * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource`
3935
+ * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileKafkaStream`
3865
3936
 
3866
3937
  You need to register one of them, and pass the profile's name.
3867
3938
  :param replace_creds: If ``True`` - override the existing credentials.
@@ -3901,6 +3972,9 @@ class MlrunProject(ModelObj):
3901
3972
  start: Optional[datetime.datetime] = None,
3902
3973
  end: Optional[datetime.datetime] = None,
3903
3974
  top_level: bool = False,
3975
+ modes: Optional[
3976
+ Union[mm_constants.EndpointMode, list[mm_constants.EndpointMode]]
3977
+ ] = None,
3904
3978
  uids: Optional[list[str]] = None,
3905
3979
  latest_only: bool = False,
3906
3980
  tsdb_metrics: bool = False,
@@ -3916,8 +3990,9 @@ class MlrunProject(ModelObj):
3916
3990
  5) function_tag
3917
3991
  6) labels
3918
3992
  7) top level
3919
- 8) uids
3920
- 9) start and end time, corresponding to the `created` field.
3993
+ 8) modes
3994
+ 9) uids
3995
+ 10) start and end time, corresponding to the `created` field.
3921
3996
  By default, when no filters are applied, all available endpoints for the given project will be listed.
3922
3997
 
3923
3998
  In addition, this functions provides a facade for listing endpoint related metrics. This facade is time-based
@@ -3937,6 +4012,8 @@ class MlrunProject(ModelObj):
3937
4012
  :param start: The start time to filter by.Corresponding to the `created` field.
3938
4013
  :param end: The end time to filter by. Corresponding to the `created` field.
3939
4014
  :param top_level: If true will return only routers and endpoint that are NOT children of any router.
4015
+ :param modes: Specifies the mode of the model endpoint. Can be "real-time" (0), "batch" (1),
4016
+ "batch_legacy" (2). If set to None, all are included.
3940
4017
  :param uids: If passed will return a list `ModelEndpoint` object with uid in uids.
3941
4018
  :param tsdb_metrics: When True, the time series metrics will be added to the output
3942
4019
  of the resulting.
@@ -3958,6 +4035,7 @@ class MlrunProject(ModelObj):
3958
4035
  start=start,
3959
4036
  end=end,
3960
4037
  top_level=top_level,
4038
+ modes=modes,
3961
4039
  uids=uids,
3962
4040
  latest_only=latest_only,
3963
4041
  tsdb_metrics=tsdb_metrics,
@@ -4052,7 +4130,12 @@ class MlrunProject(ModelObj):
4052
4130
  This ensures latest code changes are executed. This argument must be used in
4053
4131
  conjunction with the local=True argument.
4054
4132
  :param output_path: path to store artifacts, when running in a workflow this will be set automatically
4055
- :param retry: Retry configuration for the run, can be a dict or an instance of mlrun.model.Retry.
4133
+ :param retry: Retry configuration for the run, can be a dict or an instance of
4134
+ :py:class:`~mlrun.model.Retry`.
4135
+ The `count` field in the `Retry` object specifies the number of retry attempts.
4136
+ If `count=0`, the run will not be retried.
4137
+ The `backoff` field specifies the retry backoff strategy between retry attempts.
4138
+ If not provided, the default backoff delay is 30 seconds.
4056
4139
  :return: MLRun RunObject or PipelineNodeWrapper
4057
4140
  """
4058
4141
  if artifact_path:
mlrun/run.py CHANGED
@@ -17,6 +17,7 @@ import json
17
17
  import os
18
18
  import pathlib
19
19
  import socket
20
+ import sys
20
21
  import tempfile
21
22
  import time
22
23
  import typing
@@ -117,14 +118,31 @@ def function_to_module(code="", workdir=None, secrets=None, silent=False):
117
118
  raise ValueError("nothing to run, specify command or function")
118
119
 
119
120
  command = os.path.join(workdir or "", command)
120
- path = Path(command)
121
- mod_name = path.name
122
- if path.suffix:
123
- mod_name = mod_name[: -len(path.suffix)]
121
+
122
+ source_file_path_object, working_dir_path_object = (
123
+ mlrun.utils.helpers.get_source_and_working_dir_paths(command)
124
+ )
125
+ if source_file_path_object.is_relative_to(working_dir_path_object):
126
+ mod_name = mlrun.utils.helpers.get_relative_module_name_from_path(
127
+ source_file_path_object, working_dir_path_object
128
+ )
129
+ elif source_file_path_object.is_relative_to(
130
+ pathlib.Path(tempfile.gettempdir()).resolve()
131
+ ):
132
+ mod_name = Path(command).stem
133
+ else:
134
+ raise mlrun.errors.MLRunRuntimeError(
135
+ f"Cannot run source file '{command}': it must be located either under the current working "
136
+ f"directory ('{working_dir_path_object}') or the system temporary directory ('{tempfile.gettempdir()}'). "
137
+ f"This is required when running with local=True."
138
+ )
139
+
124
140
  spec = imputil.spec_from_file_location(mod_name, command)
125
141
  if spec is None:
126
142
  raise OSError(f"cannot import from {command!r}")
127
143
  mod = imputil.module_from_spec(spec)
144
+ # add to system modules, which can be necessary when running in a MockServer (ML-10937)
145
+ sys.modules[mod_name] = mod
128
146
  spec.loader.exec_module(mod)
129
147
 
130
148
  return mod
@@ -141,7 +159,7 @@ def load_func_code(command="", workdir=None, secrets=None, name="name"):
141
159
  else:
142
160
  is_remote = "://" in command
143
161
  data = get_object(command, secrets)
144
- runtime = yaml.load(data, Loader=yaml.FullLoader)
162
+ runtime = yaml.safe_load(data)
145
163
  runtime = new_function(runtime=runtime)
146
164
 
147
165
  command = runtime.spec.command or ""
@@ -222,7 +240,8 @@ def get_or_create_ctx(
222
240
  :param spec: dictionary holding run spec
223
241
  :param with_env: look for context in environment vars, default True
224
242
  :param rundb: path/url to the metadata and artifact database
225
- :param project: project to initiate the context in (by default `mlrun.mlconf.active_project`)
243
+ :param project: project to initiate the context in (by default `mlrun.mlconf.active_project`).
244
+ If not set, an active project must exist.
226
245
  :param upload_artifacts: when using local context (not as part of a job/run), upload artifacts to the
227
246
  system default artifact path location
228
247
  :return: execution context
@@ -277,6 +296,16 @@ def get_or_create_ctx(
277
296
  if newspec and not isinstance(newspec, dict):
278
297
  newspec = json.loads(newspec)
279
298
 
299
+ if (
300
+ not newspec.get("metadata", {}).get("project")
301
+ and not project
302
+ and not mlconf.active_project
303
+ ):
304
+ raise mlrun.errors.MLRunMissingProjectError(
305
+ """No active project found. Make sure to set an active project using: mlrun.get_or_create_project()
306
+ You can verify the active project with: mlrun.mlconf.active_project"""
307
+ )
308
+
280
309
  if not newspec:
281
310
  newspec = {}
282
311
  if upload_artifacts:
@@ -362,10 +391,13 @@ def import_function(url="", secrets=None, db="", project=None, new_name=None):
362
391
  return function
363
392
 
364
393
 
365
- def import_function_to_dict(url, secrets=None):
394
+ def import_function_to_dict(
395
+ url: str,
396
+ secrets: Optional[dict] = None,
397
+ ) -> dict:
366
398
  """Load function spec from local/remote YAML file"""
367
399
  obj = get_object(url, secrets)
368
- runtime = yaml.load(obj, Loader=yaml.FullLoader)
400
+ runtime = yaml.safe_load(obj)
369
401
  remote = "://" in url
370
402
 
371
403
  code = get_in(runtime, "spec.build.functionSourceCode")
@@ -388,20 +420,40 @@ def import_function_to_dict(url, secrets=None):
388
420
  raise ValueError("exec path (spec.command) must be relative")
389
421
  url = url[: url.rfind("/") + 1] + code_file
390
422
  code = get_object(url, secrets)
423
+ code_file = _ensure_path_confined_to_base_dir(
424
+ base_directory=".",
425
+ relative_path=code_file,
426
+ error_message_on_escape="Path traversal detected in spec.command",
427
+ )
391
428
  dir = path.dirname(code_file)
392
429
  if dir:
393
430
  makedirs(dir, exist_ok=True)
394
431
  with open(code_file, "wb") as fp:
395
432
  fp.write(code)
396
433
  elif cmd:
397
- if not path.isfile(code_file):
398
- # look for the file in a relative path to the yaml
399
- slash = url.rfind("/")
400
- if slash >= 0 and path.isfile(url[: url.rfind("/") + 1] + code_file):
401
- raise ValueError(
402
- f"exec file spec.command={code_file} is relative, change working dir"
403
- )
434
+ slash_index = url.rfind("/")
435
+ if slash_index < 0:
436
+ raise ValueError(f"no file in exec path (spec.command={code_file})")
437
+ base_dir = os.path.normpath(url[: slash_index + 1])
438
+
439
+ # Validate and resolve the candidate path before checking existence
440
+ candidate_path = _ensure_path_confined_to_base_dir(
441
+ base_directory=base_dir,
442
+ relative_path=code_file,
443
+ error_message_on_escape=(
444
+ f"exec file spec.command={code_file} is outside of allowed directory"
445
+ ),
446
+ )
447
+
448
+ # Only now it's safe to check file existence
449
+ if not path.isfile(candidate_path):
404
450
  raise ValueError(f"no file in exec path (spec.command={code_file})")
451
+
452
+ # Check that the path is absolute
453
+ if not os.path.isabs(code_file):
454
+ raise ValueError(
455
+ f"exec file spec.command={code_file} is relative, it must be absolute. Change working dir"
456
+ )
405
457
  else:
406
458
  raise ValueError("command or code not specified in function spec")
407
459
 
@@ -503,6 +555,7 @@ def new_function(
503
555
 
504
556
  # make sure function name is valid
505
557
  name = mlrun.utils.helpers.normalize_name(name)
558
+ mlrun.utils.helpers.validate_function_name(name)
506
559
 
507
560
  runner.metadata.name = name
508
561
  runner.metadata.project = (
@@ -542,6 +595,7 @@ def new_function(
542
595
  )
543
596
 
544
597
  runner.prepare_image_for_deploy()
598
+
545
599
  return runner
546
600
 
547
601
 
@@ -575,7 +629,7 @@ def code_to_function(
575
629
  code_output: Optional[str] = "",
576
630
  embed_code: bool = True,
577
631
  description: Optional[str] = "",
578
- requirements: Optional[Union[str, list[str]]] = None,
632
+ requirements: Optional[list[str]] = None,
579
633
  categories: Optional[list[str]] = None,
580
634
  labels: Optional[dict[str, str]] = None,
581
635
  with_doc: Optional[bool] = True,
@@ -746,6 +800,7 @@ def code_to_function(
746
800
  kind=sub_kind,
747
801
  ignored_tags=ignored_tags,
748
802
  )
803
+
749
804
  spec["spec"]["env"].append(
750
805
  {
751
806
  "name": "MLRUN_HTTPDB__NUCLIO__EXPLICIT_ACK",
@@ -798,6 +853,7 @@ def code_to_function(
798
853
  runtime.spec.build.code_origin = code_origin
799
854
  runtime.spec.build.origin_filename = filename or (name + ".ipynb")
800
855
  update_common(runtime, spec)
856
+
801
857
  return runtime
802
858
 
803
859
  if kind is None or kind in ["", "Function"]:
@@ -811,6 +867,7 @@ def code_to_function(
811
867
 
812
868
  if not name:
813
869
  raise ValueError("name must be specified")
870
+
814
871
  h = get_in(spec, "spec.handler", "").split(":")
815
872
  runtime.handler = h[0] if len(h) <= 1 else h[1]
816
873
  runtime.metadata = get_in(spec, "spec.metadata")
@@ -1184,11 +1241,13 @@ def get_model_provider(
1184
1241
  raise_missing_schema_exception=True,
1185
1242
  ) -> ModelProvider:
1186
1243
  """get mlrun dataitem object (from path/url)"""
1187
- store_manager.set(secrets, db=db)
1244
+ # without caching secrets
1245
+ store_manager.set(db=db)
1188
1246
  return store_manager.model_provider_object(
1189
1247
  url=url,
1190
1248
  default_invoke_kwargs=default_invoke_kwargs,
1191
1249
  raise_missing_schema_exception=raise_missing_schema_exception,
1250
+ secrets=secrets,
1192
1251
  )
1193
1252
 
1194
1253
 
@@ -1256,3 +1315,21 @@ def wait_for_runs_completion(
1256
1315
  runs = running
1257
1316
 
1258
1317
  return completed
1318
+
1319
+
1320
+ def _ensure_path_confined_to_base_dir(
1321
+ base_directory: str,
1322
+ relative_path: str,
1323
+ error_message_on_escape: str,
1324
+ ) -> str:
1325
+ """
1326
+ Join `user_supplied_relative_path` to `allowed_base_directory`, normalise the result,
1327
+ and guarantee it stays inside `allowed_base_directory`.
1328
+ """
1329
+ absolute_base_directory = path.abspath(base_directory)
1330
+ absolute_candidate_path = path.abspath(
1331
+ path.join(absolute_base_directory, relative_path)
1332
+ )
1333
+ if not absolute_candidate_path.startswith(absolute_base_directory + path.sep):
1334
+ raise ValueError(error_message_on_escape)
1335
+ return absolute_candidate_path
@@ -221,6 +221,24 @@ class RuntimeKinds:
221
221
  return True
222
222
  return False
223
223
 
224
+ @staticmethod
225
+ def requires_k8s_name_validation(kind: str) -> bool:
226
+ """
227
+ Returns True if the runtime kind creates Kubernetes resources that use the function name.
228
+
229
+ Function names for k8s-deployed runtimes must conform to DNS-1123 label requirements:
230
+ - Lowercase alphanumeric characters or '-'
231
+ - Start and end with an alphanumeric character
232
+ - Maximum 63 characters
233
+
234
+ Local runtimes (local, handler) run on the local machine and don't create k8s resources,
235
+ so they don't require k8s naming validation.
236
+
237
+ :param kind: Runtime kind string (job, spark, serving, local, etc.)
238
+ :return: True if function name needs k8s DNS-1123 validation, False otherwise
239
+ """
240
+ return not RuntimeKinds.is_local_runtime(kind)
241
+
224
242
  @staticmethod
225
243
  def requires_absolute_artifacts_path(kind):
226
244
  """