mlrun 1.10.0rc16__py3-none-any.whl → 1.10.1rc4__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 (101) 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 +34 -21
  21. mlrun/datastore/__init__.py +11 -3
  22. mlrun/datastore/azure_blob.py +162 -47
  23. mlrun/datastore/base.py +265 -7
  24. mlrun/datastore/datastore.py +10 -5
  25. mlrun/datastore/datastore_profile.py +61 -5
  26. mlrun/datastore/model_provider/huggingface_provider.py +367 -0
  27. mlrun/datastore/model_provider/mock_model_provider.py +87 -0
  28. mlrun/datastore/model_provider/model_provider.py +211 -74
  29. mlrun/datastore/model_provider/openai_provider.py +243 -71
  30. mlrun/datastore/s3.py +24 -2
  31. mlrun/datastore/store_resources.py +4 -4
  32. mlrun/datastore/storeytargets.py +2 -3
  33. mlrun/datastore/utils.py +15 -3
  34. mlrun/db/base.py +27 -19
  35. mlrun/db/httpdb.py +57 -48
  36. mlrun/db/nopdb.py +25 -10
  37. mlrun/execution.py +55 -13
  38. mlrun/hub/__init__.py +15 -0
  39. mlrun/hub/module.py +181 -0
  40. mlrun/k8s_utils.py +105 -16
  41. mlrun/launcher/base.py +13 -6
  42. mlrun/launcher/local.py +2 -0
  43. mlrun/model.py +9 -3
  44. mlrun/model_monitoring/api.py +66 -27
  45. mlrun/model_monitoring/applications/__init__.py +1 -1
  46. mlrun/model_monitoring/applications/base.py +388 -138
  47. mlrun/model_monitoring/applications/context.py +2 -4
  48. mlrun/model_monitoring/applications/results.py +4 -7
  49. mlrun/model_monitoring/controller.py +239 -101
  50. mlrun/model_monitoring/db/_schedules.py +36 -13
  51. mlrun/model_monitoring/db/_stats.py +4 -3
  52. mlrun/model_monitoring/db/tsdb/base.py +29 -9
  53. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
  54. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +154 -50
  55. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
  56. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
  57. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +245 -51
  58. mlrun/model_monitoring/helpers.py +28 -5
  59. mlrun/model_monitoring/stream_processing.py +45 -14
  60. mlrun/model_monitoring/writer.py +220 -1
  61. mlrun/platforms/__init__.py +3 -2
  62. mlrun/platforms/iguazio.py +7 -3
  63. mlrun/projects/operations.py +16 -11
  64. mlrun/projects/pipelines.py +2 -2
  65. mlrun/projects/project.py +157 -69
  66. mlrun/run.py +97 -20
  67. mlrun/runtimes/__init__.py +18 -0
  68. mlrun/runtimes/base.py +14 -6
  69. mlrun/runtimes/daskjob.py +1 -0
  70. mlrun/runtimes/local.py +5 -2
  71. mlrun/runtimes/mounts.py +20 -2
  72. mlrun/runtimes/nuclio/__init__.py +1 -0
  73. mlrun/runtimes/nuclio/application/application.py +147 -17
  74. mlrun/runtimes/nuclio/function.py +72 -27
  75. mlrun/runtimes/nuclio/serving.py +102 -20
  76. mlrun/runtimes/pod.py +213 -21
  77. mlrun/runtimes/utils.py +49 -9
  78. mlrun/secrets.py +54 -13
  79. mlrun/serving/remote.py +79 -6
  80. mlrun/serving/routers.py +23 -41
  81. mlrun/serving/server.py +230 -40
  82. mlrun/serving/states.py +605 -232
  83. mlrun/serving/steps.py +62 -0
  84. mlrun/serving/system_steps.py +136 -81
  85. mlrun/serving/v2_serving.py +9 -10
  86. mlrun/utils/helpers.py +215 -83
  87. mlrun/utils/logger.py +3 -1
  88. mlrun/utils/notifications/notification/base.py +18 -0
  89. mlrun/utils/notifications/notification/git.py +2 -4
  90. mlrun/utils/notifications/notification/mail.py +38 -15
  91. mlrun/utils/notifications/notification/slack.py +2 -4
  92. mlrun/utils/notifications/notification/webhook.py +2 -5
  93. mlrun/utils/notifications/notification_pusher.py +1 -1
  94. mlrun/utils/version/version.json +2 -2
  95. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/METADATA +51 -50
  96. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/RECORD +100 -95
  97. mlrun/api/schemas/__init__.py +0 -259
  98. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/WHEEL +0 -0
  99. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/entry_points.txt +0 -0
  100. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/licenses/LICENSE +0 -0
  101. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/top_level.txt +0 -0
mlrun/db/httpdb.py CHANGED
@@ -24,6 +24,7 @@ from datetime import datetime, timedelta
24
24
  from os import environ, path, remove
25
25
  from typing import Literal, Optional, Union
26
26
  from urllib.parse import urlparse
27
+ from uuid import UUID
27
28
 
28
29
  import pydantic.v1
29
30
  import requests
@@ -44,6 +45,7 @@ import mlrun.runtimes.nuclio.api_gateway
44
45
  import mlrun.runtimes.nuclio.function
45
46
  import mlrun.utils
46
47
  from mlrun.alerts.alert import AlertConfig
48
+ from mlrun.common.schemas.hub import HubSourceType
47
49
  from mlrun.db.auth_utils import OAuthClientIDTokenProvider, StaticTokenProvider
48
50
  from mlrun.errors import MLRunInvalidArgumentError, err_to_str
49
51
  from mlrun.secrets import get_secret_or_env
@@ -2554,50 +2556,6 @@ class HTTPRunDB(RunDBInterface):
2554
2556
  resp = self.api_call("GET", path, error_message)
2555
2557
  return FeatureSet.from_dict(resp.json())
2556
2558
 
2557
- def list_features(
2558
- self,
2559
- project: Optional[str] = None,
2560
- name: Optional[str] = None,
2561
- tag: Optional[str] = None,
2562
- entities: Optional[list[str]] = None,
2563
- labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2564
- ) -> list[dict]:
2565
- """List feature-sets which contain specific features. This function may return multiple versions of the same
2566
- feature-set if a specific tag is not requested. Note that the various filters of this function actually
2567
- refer to the feature-set object containing the features, not to the features themselves.
2568
-
2569
- :param project: Project which contains these features.
2570
- :param name: Name of the feature to look for. The name is used in a like query, and is not case-sensitive. For
2571
- example, looking for ``feat`` will return features which are named ``MyFeature`` as well as ``defeat``.
2572
- :param tag: Return feature-sets which contain the features looked for, and are tagged with the specific tag.
2573
- :param entities: Return only feature-sets which contain an entity whose name is contained in this list.
2574
- :param labels: Filter feature-sets by label key-value pairs or key existence. This can be provided as:
2575
- - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2576
- or `{"label": None}` to check for key existence.
2577
- - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2578
- or just `"label"` for key existence.
2579
- - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2580
- the specified key-value pairs or key existence.
2581
- :returns: A list of mapping from feature to a digest of the feature-set, which contains the feature-set
2582
- meta-data. Multiple entries may be returned for any specific feature due to multiple tags or versions
2583
- of the feature-set.
2584
- """
2585
-
2586
- project = project or config.active_project
2587
- labels = self._parse_labels(labels)
2588
- params = {
2589
- "name": name,
2590
- "tag": tag,
2591
- "entity": entities or [],
2592
- "label": labels,
2593
- }
2594
-
2595
- path = f"projects/{project}/features"
2596
-
2597
- error_message = f"Failed listing features, project: {project}, query: {params}"
2598
- resp = self.api_call("GET", path, error_message, params=params)
2599
- return resp.json()["features"]
2600
-
2601
2559
  def list_features_v2(
2602
2560
  self,
2603
2561
  project: Optional[str] = None,
@@ -3623,7 +3581,7 @@ class HTTPRunDB(RunDBInterface):
3623
3581
  intersection {"intersect_metrics":[], "intersect_results":[]}
3624
3582
  :return: A dictionary of application metrics and/or results for the model endpoints formatted by events_format.
3625
3583
  """
3626
- path = f"projects/{project}/model-endpoints/metrics"
3584
+ path = f"projects/{project}/model-monitoring/metrics"
3627
3585
  params = {
3628
3586
  "type": type,
3629
3587
  "endpoint-id": endpoint_ids,
@@ -3813,6 +3771,9 @@ class HTTPRunDB(RunDBInterface):
3813
3771
  tsdb_metrics: bool = False,
3814
3772
  metric_list: Optional[list[str]] = None,
3815
3773
  top_level: bool = False,
3774
+ modes: Optional[
3775
+ Union[mm_constants.EndpointMode, list[mm_constants.EndpointMode]]
3776
+ ] = None,
3816
3777
  uids: Optional[list[str]] = None,
3817
3778
  latest_only: bool = False,
3818
3779
  ) -> mlrun.common.schemas.ModelEndpointList:
@@ -3833,6 +3794,8 @@ class HTTPRunDB(RunDBInterface):
3833
3794
  If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
3834
3795
  will be included.
3835
3796
  :param top_level: Whether to return only top level model endpoints.
3797
+ :param modes: Specifies the modes of the model endpoints. Can be "real-time" (0), "batch" (1),
3798
+ "batch_legacy" (2). If set to None, all are included.
3836
3799
  :param uids: A list of unique ids to filter by.
3837
3800
  :param latest_only: Whether to return only the latest model endpoint version.
3838
3801
  :return: A list of model endpoints.
@@ -3841,6 +3804,13 @@ class HTTPRunDB(RunDBInterface):
3841
3804
  labels = self._parse_labels(labels)
3842
3805
  if names and isinstance(names, str):
3843
3806
  names = [names]
3807
+ if modes:
3808
+ # Ensure backward compatibility with Python 3.9 clients by converting IntEnum modes to integer values
3809
+ modes = (
3810
+ [modes.value]
3811
+ if isinstance(modes, mm_constants.EndpointMode)
3812
+ else [mode.value for mode in modes]
3813
+ )
3844
3814
  response = self.api_call(
3845
3815
  method=mlrun.common.types.HTTPMethod.GET,
3846
3816
  path=path,
@@ -3856,6 +3826,7 @@ class HTTPRunDB(RunDBInterface):
3856
3826
  "tsdb-metrics": tsdb_metrics,
3857
3827
  "metric": metric_list,
3858
3828
  "top-level": top_level,
3829
+ "mode": modes,
3859
3830
  "uid": uids,
3860
3831
  "latest-only": latest_only,
3861
3832
  },
@@ -3964,6 +3935,13 @@ class HTTPRunDB(RunDBInterface):
3964
3935
  raise MLRunInvalidArgumentError(
3965
3936
  "Either endpoint_uid or function_name and function_tag must be provided"
3966
3937
  )
3938
+ if uid:
3939
+ try:
3940
+ UUID(uid)
3941
+ except (ValueError, TypeError):
3942
+ raise MLRunInvalidArgumentError(
3943
+ "endpoint_id must be a valid UUID string"
3944
+ )
3967
3945
 
3968
3946
  def update_model_monitoring_controller(
3969
3947
  self,
@@ -4111,7 +4089,7 @@ class HTTPRunDB(RunDBInterface):
4111
4089
  response = self.api_call(
4112
4090
  method=mlrun.common.types.HTTPMethod.DELETE,
4113
4091
  path=f"projects/{project}/model-monitoring/functions",
4114
- params={"functions": functions},
4092
+ params={"function": functions},
4115
4093
  )
4116
4094
  deletion_failed = False
4117
4095
  if response.status_code == http.HTTPStatus.ACCEPTED:
@@ -4153,6 +4131,26 @@ class HTTPRunDB(RunDBInterface):
4153
4131
  params={**credentials, "replace_creds": replace_creds},
4154
4132
  )
4155
4133
 
4134
+ def delete_model_monitoring_metrics(
4135
+ self,
4136
+ project: str,
4137
+ application_name: str,
4138
+ endpoint_ids: Optional[list[str]] = None,
4139
+ ) -> None:
4140
+ """
4141
+ Delete model endpoints metrics values.
4142
+
4143
+ :param project: The name of the project.
4144
+ :param application_name: The name of the application.
4145
+ :param endpoint_ids: The unique IDs of the model endpoints to delete metrics values from. If none is
4146
+ provided, the metrics values will be deleted from all project's model endpoints.
4147
+ """
4148
+ self.api_call(
4149
+ method=mlrun.common.types.HTTPMethod.DELETE,
4150
+ path=f"projects/{project}/model-monitoring/metrics",
4151
+ params={"endpoint-id": endpoint_ids, "application-name": application_name},
4152
+ )
4153
+
4156
4154
  def get_monitoring_function_summaries(
4157
4155
  self,
4158
4156
  project: str,
@@ -4319,6 +4317,7 @@ class HTTPRunDB(RunDBInterface):
4319
4317
  item_name: Optional[str] = None,
4320
4318
  tag: Optional[str] = None,
4321
4319
  version: Optional[str] = None,
4320
+ item_type: HubSourceType = HubSourceType.functions,
4322
4321
  ) -> list[mlrun.common.schemas.hub.IndexedHubSource]:
4323
4322
  """
4324
4323
  List hub sources in the MLRun DB.
@@ -4326,6 +4325,7 @@ class HTTPRunDB(RunDBInterface):
4326
4325
  :param item_name: Sources contain this item will be returned, If not provided all sources will be returned.
4327
4326
  :param tag: Item tag to filter by, supported only if item name is provided.
4328
4327
  :param version: Item version to filter by, supported only if item name is provided and tag is not.
4328
+ :param item_type: Item type to filter by, supported only if item name is provided.
4329
4329
 
4330
4330
  :returns: List of indexed hub sources.
4331
4331
  """
@@ -4333,6 +4333,7 @@ class HTTPRunDB(RunDBInterface):
4333
4333
  params = {}
4334
4334
  if item_name:
4335
4335
  params["item-name"] = normalize_name(item_name)
4336
+ params["item-type"] = item_type
4336
4337
  if tag:
4337
4338
  params["tag"] = tag
4338
4339
  if version:
@@ -4371,6 +4372,7 @@ class HTTPRunDB(RunDBInterface):
4371
4372
  version: Optional[str] = None,
4372
4373
  tag: Optional[str] = None,
4373
4374
  force_refresh: bool = False,
4375
+ object_type: HubSourceType = HubSourceType.functions,
4374
4376
  ):
4375
4377
  """
4376
4378
  Retrieve the item catalog for a specified hub source.
@@ -4383,6 +4385,7 @@ class HTTPRunDB(RunDBInterface):
4383
4385
  rather than rely on cached information which may exist from previous get requests. For example,
4384
4386
  if the source was re-built,
4385
4387
  this will make the server get the updated information. Default is ``False``.
4388
+ :param object_type: Type of object to retrieve from the hub source (e.g: functions, modules).
4386
4389
  :returns: :py:class:`~mlrun.common.schemas.hub.HubCatalog` object, which is essentially a list
4387
4390
  of :py:class:`~mlrun.common.schemas.hub.HubItem` entries.
4388
4391
  """
@@ -4391,6 +4394,7 @@ class HTTPRunDB(RunDBInterface):
4391
4394
  "version": version,
4392
4395
  "tag": tag,
4393
4396
  "force-refresh": force_refresh,
4397
+ "object_type": object_type,
4394
4398
  }
4395
4399
  response = self.api_call(method="GET", path=path, params=params)
4396
4400
  return mlrun.common.schemas.HubCatalog(**response.json())
@@ -4402,6 +4406,7 @@ class HTTPRunDB(RunDBInterface):
4402
4406
  version: Optional[str] = None,
4403
4407
  tag: str = "latest",
4404
4408
  force_refresh: bool = False,
4409
+ item_type: HubSourceType = HubSourceType.functions,
4405
4410
  ):
4406
4411
  """
4407
4412
  Retrieve a specific hub item.
@@ -4413,6 +4418,7 @@ class HTTPRunDB(RunDBInterface):
4413
4418
  :param force_refresh: Make the server fetch the information from the actual hub
4414
4419
  source, rather than
4415
4420
  rely on cached information. Default is ``False``.
4421
+ :param item_type: The type of item to retrieve from the hub source (e.g: functions, modules).
4416
4422
  :returns: :py:class:`~mlrun.common.schemas.hub.HubItem`.
4417
4423
  """
4418
4424
  path = (f"hub/sources/{source_name}/items/{item_name}",)
@@ -4420,6 +4426,7 @@ class HTTPRunDB(RunDBInterface):
4420
4426
  "version": version,
4421
4427
  "tag": tag,
4422
4428
  "force-refresh": force_refresh,
4429
+ "item_type": item_type,
4423
4430
  }
4424
4431
  response = self.api_call(method="GET", path=path, params=params)
4425
4432
  return mlrun.common.schemas.HubItem(**response.json())
@@ -4431,6 +4438,7 @@ class HTTPRunDB(RunDBInterface):
4431
4438
  asset_name: str,
4432
4439
  version: Optional[str] = None,
4433
4440
  tag: str = "latest",
4441
+ item_type: HubSourceType = HubSourceType.functions,
4434
4442
  ):
4435
4443
  """
4436
4444
  Get hub asset from item.
@@ -4440,13 +4448,14 @@ class HTTPRunDB(RunDBInterface):
4440
4448
  :param asset_name: Name of the asset to retrieve.
4441
4449
  :param version: Get a specific version of the item. Default is ``None``.
4442
4450
  :param tag: Get a specific version of the item identified by tag. Default is ``latest``.
4443
-
4451
+ :param item_type: The type of item to retrieve from the hub source (e.g: functions, modules).
4444
4452
  :returns: http response with the asset in the content attribute
4445
4453
  """
4446
4454
  path = f"hub/sources/{source_name}/items/{item_name}/assets/{asset_name}"
4447
4455
  params = {
4448
4456
  "version": version,
4449
4457
  "tag": tag,
4458
+ "item_type": item_type,
4450
4459
  }
4451
4460
  response = self.api_call(method="GET", path=path, params=params)
4452
4461
  return response
@@ -5201,7 +5210,7 @@ class HTTPRunDB(RunDBInterface):
5201
5210
 
5202
5211
  :return: A ModelEndpointDriftValues object containing the drift counts over time.
5203
5212
  """
5204
- endpoint_path = f"projects/{project}/model-endpoints/drift-over-time"
5213
+ endpoint_path = f"projects/{project}/model-monitoring/drift-over-time"
5205
5214
  error_message = f"Failed retrieving drift data for {project}"
5206
5215
  response = self.api_call(
5207
5216
  method="GET",
mlrun/db/nopdb.py CHANGED
@@ -376,16 +376,6 @@ class NopDB(RunDBInterface):
376
376
  ) -> dict:
377
377
  pass
378
378
 
379
- def list_features(
380
- self,
381
- project: str,
382
- name: Optional[str] = None,
383
- tag: Optional[str] = None,
384
- entities: Optional[list[str]] = None,
385
- labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
386
- ) -> mlrun.common.schemas.FeaturesOutput:
387
- pass
388
-
389
379
  def list_features_v2(
390
380
  self,
391
381
  project: str,
@@ -636,6 +626,9 @@ class NopDB(RunDBInterface):
636
626
  tsdb_metrics: bool = False,
637
627
  metric_list: Optional[list[str]] = None,
638
628
  top_level: bool = False,
629
+ modes: Optional[
630
+ Union[mm_constants.EndpointMode, list[mm_constants.EndpointMode]]
631
+ ] = None,
639
632
  uids: Optional[list[str]] = None,
640
633
  latest_only: bool = False,
641
634
  ) -> mlrun.common.schemas.ModelEndpointList:
@@ -682,6 +675,7 @@ class NopDB(RunDBInterface):
682
675
  item_name: Optional[str] = None,
683
676
  tag: Optional[str] = None,
684
677
  version: Optional[str] = None,
678
+ item_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
685
679
  ):
686
680
  pass
687
681
 
@@ -698,6 +692,7 @@ class NopDB(RunDBInterface):
698
692
  version: Optional[str] = None,
699
693
  tag: Optional[str] = None,
700
694
  force_refresh: bool = False,
695
+ object_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
701
696
  ):
702
697
  pass
703
698
 
@@ -709,6 +704,18 @@ class NopDB(RunDBInterface):
709
704
  version: Optional[str] = None,
710
705
  tag: str = "latest",
711
706
  force_refresh: bool = False,
707
+ item_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
708
+ ):
709
+ pass
710
+
711
+ def get_hub_asset(
712
+ self,
713
+ source_name: str,
714
+ item_name: str,
715
+ asset_name: str,
716
+ version: Optional[str] = None,
717
+ tag: str = "latest",
718
+ item_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
712
719
  ):
713
720
  pass
714
721
 
@@ -895,6 +902,14 @@ class NopDB(RunDBInterface):
895
902
  ) -> None:
896
903
  pass
897
904
 
905
+ def delete_model_monitoring_metrics(
906
+ self,
907
+ project: str,
908
+ application_name: str,
909
+ endpoint_ids: Optional[list[str]] = None,
910
+ ) -> None:
911
+ pass
912
+
898
913
  def get_monitoring_function_summaries(
899
914
  self,
900
915
  project: str,
mlrun/execution.py CHANGED
@@ -917,7 +917,7 @@ class MLClientCtx:
917
917
  prompt_path: Optional[str] = None,
918
918
  prompt_legend: Optional[dict] = None,
919
919
  model_artifact: Union[ModelArtifact, str] = None,
920
- model_configuration: Optional[dict] = None,
920
+ invocation_config: Optional[dict] = None,
921
921
  description: Optional[str] = None,
922
922
  target_path: Optional[str] = None,
923
923
  artifact_path: Optional[str] = None,
@@ -934,14 +934,51 @@ class MLClientCtx:
934
934
 
935
935
  Examples::
936
936
 
937
- # Log an inline prompt
937
+ # Log directly with an inline prompt template
938
938
  context.log_llm_prompt(
939
- key="qa-prompt",
940
- prompt_template=[{"role: "user", "content": "question with {place_holder}"}],
939
+ key="customer_support_prompt",
940
+ prompt_template=[
941
+ {
942
+ "role": "system",
943
+ "content": "You are a helpful customer support assistant.",
944
+ },
945
+ {
946
+ "role": "user",
947
+ "content": "The customer reports: {issue_description}",
948
+ },
949
+ ],
950
+ prompt_legend={
951
+ "issue_description": {
952
+ "field": "user_issue",
953
+ "description": "Detailed description of the customer's issue",
954
+ },
955
+ "solution": {
956
+ "field": "proposed_solution",
957
+ "description": "Suggested fix for the customer's issue",
958
+ },
959
+ },
941
960
  model_artifact=model,
942
- prompt_legend={"question": "user_input"},
943
- model_configuration={"temperature": 0.7, "max_tokens": 128},
944
- tag="latest",
961
+ invocation_config={"temperature": 0.5, "max_tokens": 200},
962
+ description="Prompt for handling customer support queries",
963
+ tag="support-v1",
964
+ labels={"domain": "support"},
965
+ )
966
+
967
+ # Log a prompt from file
968
+ context.log_llm_prompt(
969
+ key="qa_prompt",
970
+ prompt_path="prompts/template.json",
971
+ prompt_legend={
972
+ "question": {
973
+ "field": "user_question",
974
+ "description": "The actual question asked by the user",
975
+ }
976
+ },
977
+ model_artifact=model,
978
+ invocation_config={"temperature": 0.7, "max_tokens": 256},
979
+ description="Q&A prompt template with user-provided question",
980
+ tag="v2",
981
+ labels={"task": "qa", "stage": "experiment"},
945
982
  )
946
983
 
947
984
  :param key: Unique name of the artifact.
@@ -950,17 +987,22 @@ class MLClientCtx:
950
987
  "role": "user", "content": "I need your help with {profession}"]. only "role" and "content" keys allow in any
951
988
  str format (upper/lower case), keys will be modified to lower case.
952
989
  Cannot be used with `prompt_path`.
953
- :param prompt_path: Path to a file containing the prompt content. Cannot be used with `prompt_string`.
990
+ :param prompt_path: Path to a JSON file containing the prompt template.
991
+ Cannot be used together with `prompt_template`.
992
+ The file should define a list of dictionaries in the same format
993
+ supported by `prompt_template`.
954
994
  :param prompt_legend: A dictionary where each key is a placeholder in the prompt (e.g., ``{user_name}``)
955
995
  and the value is a dictionary holding two keys, "field", "description". "field" points to the field in
956
996
  the event where the value of the place-holder inside the event, if None or not exist will be replaced
957
997
  with the place-holder name. "description" will point to explanation of what that placeholder represents.
958
998
  Useful for documenting and clarifying dynamic parts of the prompt.
959
999
  :param model_artifact: Reference to the parent model (either `ModelArtifact` or model URI string).
960
- :param model_configuration: Dictionary of generation parameters (e.g., temperature, max_tokens).
961
- :param description: Optional description of the prompt.
962
- :param target_path: Path to write the artifact locally.
963
- :param artifact_path: Path in the artifact store (defaults to project artifact path).
1000
+ :param invocation_config: Dictionary of generation parameters (e.g., temperature, max_tokens).
1001
+ :param description: Optional description of the prompt.
1002
+ :param target_path: Absolute target path (instead of using artifact_path + local_path)
1003
+ :param artifact_path: Target artifact path (when not using the default)
1004
+ To define a subpath under the default location use:
1005
+ `artifact_path=context.artifact_subpath('data')`
964
1006
  :param tag: Tag/version to assign to the prompt artifact.
965
1007
  :param labels: Labels to tag the artifact (e.g., list or dict of key-value pairs).
966
1008
  :param upload: Whether to upload the artifact to the store (defaults to True).
@@ -981,7 +1023,7 @@ class MLClientCtx:
981
1023
  prompt_path=prompt_path,
982
1024
  prompt_legend=prompt_legend,
983
1025
  model_artifact=model_artifact,
984
- model_configuration=model_configuration,
1026
+ invocation_config=invocation_config,
985
1027
  target_path=target_path,
986
1028
  description=description,
987
1029
  **kwargs,
mlrun/hub/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ # Copyright 2025 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .module import get_hub_module, import_module
mlrun/hub/module.py ADDED
@@ -0,0 +1,181 @@
1
+ # Copyright 2025 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import os
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+ from typing import Optional, Union
20
+
21
+ import yaml
22
+
23
+ import mlrun.common.types
24
+ import mlrun.utils
25
+ from mlrun.common.schemas.hub import HubModuleType, HubSourceType
26
+ from mlrun.run import function_to_module, get_object
27
+ from mlrun.utils import logger
28
+
29
+ from ..errors import MLRunBadRequestError
30
+ from ..model import ModelObj
31
+ from ..utils import extend_hub_uri_if_needed
32
+
33
+
34
+ class HubModule(ModelObj):
35
+ def __init__(
36
+ self,
37
+ name: str,
38
+ kind: Union[HubModuleType, str],
39
+ version: Optional[str] = None,
40
+ description: Optional[str] = None,
41
+ categories: Optional[list] = None,
42
+ requirements: Optional[list] = None,
43
+ local_path: Optional[str] = None,
44
+ filename: Optional[str] = None,
45
+ example: Optional[str] = None,
46
+ url: Optional[str] = None,
47
+ **kwargs, # catch all for unused args
48
+ ):
49
+ self.name: str = name
50
+ self.version: str = version
51
+ self.kind: HubModuleType = kind
52
+ self.description: str = description or ""
53
+ self.categories: list = categories or []
54
+ self.requirements: list = requirements or []
55
+ self.local_path: str = local_path or ""
56
+ self.filename: str = filename or name + ".py"
57
+ self.example: str = example or ""
58
+ self.url: str = url or ""
59
+
60
+ def module(self):
61
+ """Import the module after downloading its fils to local_path"""
62
+ try:
63
+ return function_to_module(code=self.filename, workdir=self.local_path)
64
+ except FileNotFoundError:
65
+ searched_path = self.local_path or "./"
66
+ raise FileNotFoundError(
67
+ f"Module file {self.filename} not found in {searched_path}, try calling download_module_files() first"
68
+ )
69
+
70
+ def install_requirements(self) -> None:
71
+ """
72
+ Install pip-style requirements (e.g., ["pandas>=2.0.0", "requests==2.31.0"]).
73
+ """
74
+ for req in self.requirements:
75
+ logger.info(f"Installing {req} ...")
76
+ try:
77
+ subprocess.run(
78
+ [sys.executable, "-m", "pip", "install", req], check=True, text=True
79
+ )
80
+ logger.info(f"Installed {req}")
81
+ except subprocess.CalledProcessError as e:
82
+ logger.error(f"Failed to install {req} (exit code {e.returncode})")
83
+
84
+ def download_module_files(self, local_path=None, secrets=None):
85
+ """
86
+ Download this hub module’s files (code file and, if available, an example notebook) to the target directory
87
+ specified by `local_path` (defaults to the current working directory).
88
+ This path will be used later to locate the code file when importing the module.
89
+ """
90
+ self.local_path = self.verify_directory(path=local_path)
91
+ source_url, _ = extend_hub_uri_if_needed(
92
+ uri=self.url, asset_type=HubSourceType.modules, file=self.filename
93
+ )
94
+ self._download_object(
95
+ obj_url=source_url, target_name=self.filename, secrets=secrets
96
+ )
97
+ if self.example:
98
+ example_url, _ = extend_hub_uri_if_needed(
99
+ uri=self.url, asset_type=HubSourceType.modules, file=self.example
100
+ )
101
+ self._download_object(
102
+ obj_url=example_url, target_name=self.example, secrets=secrets
103
+ )
104
+
105
+ def _download_object(self, obj_url, target_name, secrets=None):
106
+ data = get_object(url=obj_url, secrets=secrets)
107
+ target_dir = self.local_path if self.local_path is not None else os.getcwd()
108
+ target_filepath = os.path.join(target_dir, target_name)
109
+ with open(target_filepath, "wb") as f:
110
+ f.write(data)
111
+
112
+ @staticmethod
113
+ def verify_directory(path: Optional[str] = None) -> Path:
114
+ """
115
+ Validate that the given path is an existing directory.
116
+ If no path has been provided, returns current working directory.
117
+ """
118
+ if path:
119
+ path = Path(path)
120
+ if not path.exists():
121
+ raise ValueError(f"Path does not exist: {path}")
122
+ if not path.is_dir():
123
+ raise ValueError(f"Path is not a directory: {path}")
124
+ return path
125
+ return Path(os.getcwd())
126
+
127
+ def get_module_file_path(self):
128
+ if not self.local_path:
129
+ raise MLRunBadRequestError(
130
+ "module files haven't been downloaded yet, try calling download_module_files() first"
131
+ )
132
+ return str(Path(self.local_path) / self.filename)
133
+
134
+
135
+ def get_hub_module(
136
+ url: str = "",
137
+ download_files: Optional[bool] = True,
138
+ secrets: Optional[dict] = None,
139
+ local_path: Optional[str] = None,
140
+ ) -> HubModule:
141
+ """
142
+ Get a hub-module object containing metadata of the requested module.
143
+ :param url: Hub module url in the format "hub://[<source>/]<item-name>[:<tag>]"
144
+ :param download_files: When set to True, the module files (code file and example notebook) are downloaded
145
+ :param secrets: Optional, credentials dict for DB or URL (s3, v3io, ...)
146
+ :param local_path: Path to target directory for the module files. Ignored when download_files is set to False.
147
+ Defaults to the current working directory.
148
+
149
+ :return: HubModule object
150
+ """
151
+ item_yaml_url, is_hub_uri = extend_hub_uri_if_needed(
152
+ uri=url, asset_type=HubSourceType.modules, file="item.yaml"
153
+ )
154
+ if not is_hub_uri:
155
+ raise mlrun.errors.MLRunInvalidArgumentError("Not a valid hub URL")
156
+ yaml_obj = get_object(url=item_yaml_url, secrets=secrets)
157
+ item_yaml = yaml.safe_load(yaml_obj)
158
+ spec = item_yaml.pop("spec", {})
159
+ hub_module = HubModule(**item_yaml, **spec, url=url)
160
+ if download_files:
161
+ hub_module.download_module_files(local_path=local_path, secrets=secrets)
162
+ return hub_module
163
+
164
+
165
+ def import_module(url="", install_requirements=False, secrets=None, local_path=None):
166
+ """
167
+ Import a module from the hub to use directly.
168
+ :param url: hub module url in the format "hub://[<source>/]<item-name>[:<tag>]"
169
+ :param install_requirements: when set to True, the module's requirements are installed.
170
+ :param secrets: optional, credentials dict for DB or URL (s3, v3io, ...)
171
+ :param local_path: Path to target directory for the module files (code and example notebook).
172
+ Defaults to the current working directory.
173
+
174
+ :return: the module
175
+ """
176
+ hub_module: HubModule = get_hub_module(
177
+ url=url, download_files=True, secrets=secrets, local_path=local_path
178
+ )
179
+ if install_requirements:
180
+ hub_module.install_requirements()
181
+ return hub_module.module()