mlrun 1.10.0rc40__py3-none-any.whl → 1.11.0rc16__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 (150) hide show
  1. mlrun/__init__.py +3 -2
  2. mlrun/__main__.py +0 -4
  3. mlrun/artifacts/dataset.py +2 -2
  4. mlrun/artifacts/plots.py +1 -1
  5. mlrun/{model_monitoring/db/tsdb/tdengine → auth}/__init__.py +2 -3
  6. mlrun/auth/nuclio.py +89 -0
  7. mlrun/auth/providers.py +429 -0
  8. mlrun/auth/utils.py +415 -0
  9. mlrun/common/constants.py +7 -0
  10. mlrun/common/model_monitoring/helpers.py +41 -4
  11. mlrun/common/runtimes/constants.py +28 -0
  12. mlrun/common/schemas/__init__.py +13 -3
  13. mlrun/common/schemas/alert.py +2 -2
  14. mlrun/common/schemas/api_gateway.py +3 -0
  15. mlrun/common/schemas/auth.py +10 -10
  16. mlrun/common/schemas/client_spec.py +4 -0
  17. mlrun/common/schemas/constants.py +25 -0
  18. mlrun/common/schemas/frontend_spec.py +1 -8
  19. mlrun/common/schemas/function.py +24 -0
  20. mlrun/common/schemas/hub.py +3 -2
  21. mlrun/common/schemas/model_monitoring/__init__.py +1 -1
  22. mlrun/common/schemas/model_monitoring/constants.py +2 -2
  23. mlrun/common/schemas/secret.py +17 -2
  24. mlrun/common/secrets.py +95 -1
  25. mlrun/common/types.py +10 -10
  26. mlrun/config.py +53 -15
  27. mlrun/data_types/infer.py +2 -2
  28. mlrun/datastore/__init__.py +2 -3
  29. mlrun/datastore/base.py +274 -10
  30. mlrun/datastore/datastore.py +1 -1
  31. mlrun/datastore/datastore_profile.py +49 -17
  32. mlrun/datastore/model_provider/huggingface_provider.py +6 -2
  33. mlrun/datastore/model_provider/model_provider.py +2 -2
  34. mlrun/datastore/model_provider/openai_provider.py +2 -2
  35. mlrun/datastore/s3.py +15 -16
  36. mlrun/datastore/sources.py +1 -1
  37. mlrun/datastore/store_resources.py +4 -4
  38. mlrun/datastore/storeytargets.py +16 -10
  39. mlrun/datastore/targets.py +1 -1
  40. mlrun/datastore/utils.py +16 -3
  41. mlrun/datastore/v3io.py +1 -1
  42. mlrun/db/base.py +36 -12
  43. mlrun/db/httpdb.py +316 -101
  44. mlrun/db/nopdb.py +29 -11
  45. mlrun/errors.py +4 -2
  46. mlrun/execution.py +11 -12
  47. mlrun/feature_store/api.py +1 -1
  48. mlrun/feature_store/common.py +1 -1
  49. mlrun/feature_store/feature_vector_utils.py +1 -1
  50. mlrun/feature_store/steps.py +8 -6
  51. mlrun/frameworks/_common/utils.py +3 -3
  52. mlrun/frameworks/_dl_common/loggers/logger.py +1 -1
  53. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +2 -1
  54. mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +1 -1
  55. mlrun/frameworks/_ml_common/utils.py +2 -1
  56. mlrun/frameworks/auto_mlrun/auto_mlrun.py +4 -3
  57. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +2 -1
  58. mlrun/frameworks/onnx/dataset.py +2 -1
  59. mlrun/frameworks/onnx/mlrun_interface.py +2 -1
  60. mlrun/frameworks/pytorch/callbacks/logging_callback.py +5 -4
  61. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +2 -1
  62. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +2 -1
  63. mlrun/frameworks/pytorch/utils.py +2 -1
  64. mlrun/frameworks/sklearn/metric.py +2 -1
  65. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +5 -4
  66. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +2 -1
  67. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +2 -1
  68. mlrun/hub/__init__.py +37 -0
  69. mlrun/hub/base.py +142 -0
  70. mlrun/hub/module.py +67 -76
  71. mlrun/hub/step.py +113 -0
  72. mlrun/launcher/base.py +2 -1
  73. mlrun/launcher/local.py +2 -1
  74. mlrun/model.py +12 -2
  75. mlrun/model_monitoring/__init__.py +0 -1
  76. mlrun/model_monitoring/api.py +2 -2
  77. mlrun/model_monitoring/applications/base.py +20 -6
  78. mlrun/model_monitoring/applications/context.py +1 -0
  79. mlrun/model_monitoring/controller.py +7 -17
  80. mlrun/model_monitoring/db/_schedules.py +2 -16
  81. mlrun/model_monitoring/db/_stats.py +2 -13
  82. mlrun/model_monitoring/db/tsdb/__init__.py +9 -7
  83. mlrun/model_monitoring/db/tsdb/base.py +2 -4
  84. mlrun/model_monitoring/db/tsdb/preaggregate.py +234 -0
  85. mlrun/model_monitoring/db/tsdb/stream_graph_steps.py +63 -0
  86. mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_metrics_queries.py +414 -0
  87. mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_predictions_queries.py +376 -0
  88. mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_results_queries.py +590 -0
  89. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connection.py +434 -0
  90. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connector.py +541 -0
  91. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_operations.py +808 -0
  92. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_schema.py +502 -0
  93. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream.py +163 -0
  94. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream_graph_steps.py +60 -0
  95. mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_dataframe_processor.py +141 -0
  96. mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_query_builder.py +585 -0
  97. mlrun/model_monitoring/db/tsdb/timescaledb/writer_graph_steps.py +73 -0
  98. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +4 -6
  99. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +147 -79
  100. mlrun/model_monitoring/features_drift_table.py +2 -1
  101. mlrun/model_monitoring/helpers.py +2 -1
  102. mlrun/model_monitoring/stream_processing.py +18 -16
  103. mlrun/model_monitoring/writer.py +4 -3
  104. mlrun/package/__init__.py +2 -1
  105. mlrun/platforms/__init__.py +0 -44
  106. mlrun/platforms/iguazio.py +1 -1
  107. mlrun/projects/operations.py +11 -10
  108. mlrun/projects/project.py +81 -82
  109. mlrun/run.py +4 -7
  110. mlrun/runtimes/__init__.py +2 -204
  111. mlrun/runtimes/base.py +89 -21
  112. mlrun/runtimes/constants.py +225 -0
  113. mlrun/runtimes/daskjob.py +4 -2
  114. mlrun/runtimes/databricks_job/databricks_runtime.py +2 -1
  115. mlrun/runtimes/mounts.py +5 -0
  116. mlrun/runtimes/nuclio/__init__.py +12 -8
  117. mlrun/runtimes/nuclio/api_gateway.py +36 -6
  118. mlrun/runtimes/nuclio/application/application.py +200 -32
  119. mlrun/runtimes/nuclio/function.py +154 -49
  120. mlrun/runtimes/nuclio/serving.py +55 -42
  121. mlrun/runtimes/pod.py +59 -10
  122. mlrun/secrets.py +46 -2
  123. mlrun/serving/__init__.py +2 -0
  124. mlrun/serving/remote.py +5 -5
  125. mlrun/serving/routers.py +3 -3
  126. mlrun/serving/server.py +46 -43
  127. mlrun/serving/serving_wrapper.py +6 -2
  128. mlrun/serving/states.py +554 -207
  129. mlrun/serving/steps.py +1 -1
  130. mlrun/serving/system_steps.py +42 -33
  131. mlrun/track/trackers/mlflow_tracker.py +29 -31
  132. mlrun/utils/helpers.py +89 -16
  133. mlrun/utils/http.py +9 -2
  134. mlrun/utils/notifications/notification/git.py +1 -1
  135. mlrun/utils/notifications/notification/mail.py +39 -16
  136. mlrun/utils/notifications/notification_pusher.py +2 -2
  137. mlrun/utils/version/version.json +2 -2
  138. mlrun/utils/version/version.py +3 -4
  139. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/METADATA +39 -49
  140. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/RECORD +144 -130
  141. mlrun/db/auth_utils.py +0 -152
  142. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +0 -343
  143. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -75
  144. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +0 -281
  145. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +0 -1368
  146. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +0 -51
  147. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/WHEEL +0 -0
  148. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/entry_points.txt +0 -0
  149. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/licenses/LICENSE +0 -0
  150. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/top_level.txt +0 -0
mlrun/errors.py CHANGED
@@ -264,12 +264,14 @@ class MLRunFatalFailureError(Exception):
264
264
 
265
265
 
266
266
  class ModelRunnerError(MLRunBaseError):
267
- def __init__(self, models_errors: dict[str:str], *args) -> None:
267
+ def __init__(self, models_errors: dict[str, str], *args) -> None:
268
268
  self.models_errors = models_errors
269
269
  super().__init__(self.__repr__(), *args)
270
270
 
271
271
  def __repr__(self):
272
- return f"ModelRunnerError: {repr(self.models_errors)}"
272
+ return "ModelRunnerError: " + ";\n".join(
273
+ f"{model} {msg}" for model, msg in self.models_errors.items()
274
+ )
273
275
 
274
276
  def __copy__(self):
275
277
  return type(self)(models_errors=self.models_errors)
mlrun/execution.py CHANGED
@@ -15,7 +15,6 @@
15
15
  import logging
16
16
  import os
17
17
  import uuid
18
- import warnings
19
18
  from copy import deepcopy
20
19
  from typing import Optional, Union, cast
21
20
 
@@ -101,6 +100,7 @@ class MLClientCtx:
101
100
  self._node_selector = {}
102
101
  self._tolerations = {}
103
102
  self._affinity = {}
103
+ self._auth = {}
104
104
 
105
105
  self._function = ""
106
106
  self._parameters = {}
@@ -234,6 +234,11 @@ class MLClientCtx:
234
234
  """Dictionary with node selectors (read-only)"""
235
235
  return deepcopy(self._node_selector)
236
236
 
237
+ @property
238
+ def auth(self):
239
+ """Dictionary with auth (read-only)"""
240
+ return deepcopy(self._auth)
241
+
237
242
  @property
238
243
  def tolerations(self):
239
244
  """Dictionary with tolerations (read-only)"""
@@ -437,6 +442,7 @@ class MLClientCtx:
437
442
  self._affinity = spec.get("affinity", self._affinity)
438
443
  self._reset_on_run = spec.get("reset_on_run", self._reset_on_run)
439
444
  self._retry_spec = spec.get("retry", self._retry_spec)
445
+ self._auth = spec.get("auth", self._auth)
440
446
 
441
447
  self._init_dbs(rundb)
442
448
 
@@ -1141,14 +1147,6 @@ class MLClientCtx:
1141
1147
  self._update_run()
1142
1148
  return item
1143
1149
 
1144
- def get_cached_artifact(self, key):
1145
- """Return a logged artifact from cache (for potential updates)"""
1146
- warnings.warn(
1147
- "get_cached_artifact is deprecated in 1.8.0 and will be removed in 1.11.0. Use get_artifact instead.",
1148
- FutureWarning,
1149
- )
1150
- return self.get_artifact(key)
1151
-
1152
1150
  def get_artifact(
1153
1151
  self, key, tag=None, iter=None, tree=None, uid=None
1154
1152
  ) -> Optional[Artifact]:
@@ -1310,6 +1308,7 @@ class MLClientCtx:
1310
1308
  "node_selector": self._node_selector,
1311
1309
  "tolerations": self._tolerations,
1312
1310
  "affinity": self._affinity,
1311
+ "auth": self._auth,
1313
1312
  "retry": self._retry_spec,
1314
1313
  },
1315
1314
  "status": {
@@ -1511,15 +1510,15 @@ class MLClientCtx:
1511
1510
 
1512
1511
 
1513
1512
  def _cast_result(value):
1514
- if isinstance(value, (int, str, float)):
1513
+ if isinstance(value, int | str | float):
1515
1514
  return value
1516
1515
  if isinstance(value, list):
1517
1516
  return [_cast_result(v) for v in value]
1518
1517
  if isinstance(value, dict):
1519
1518
  return {k: _cast_result(v) for k, v in value.items()}
1520
- if isinstance(value, (np.int64, np.integer)):
1519
+ if isinstance(value, np.int64 | np.integer):
1521
1520
  return int(value)
1522
- if isinstance(value, (np.floating, np.float64)):
1521
+ if isinstance(value, np.floating | np.float64):
1523
1522
  return float(value)
1524
1523
  if isinstance(value, np.ndarray):
1525
1524
  return value.tolist()
@@ -361,7 +361,7 @@ def _ingest(
361
361
  import pyspark.sql
362
362
 
363
363
  if (
364
- isinstance(source, (pd.DataFrame, pyspark.sql.DataFrame))
364
+ isinstance(source, pd.DataFrame | pyspark.sql.DataFrame)
365
365
  and run_config is not None
366
366
  ):
367
367
  raise mlrun.errors.MLRunInvalidArgumentError(
@@ -256,7 +256,7 @@ class RunConfig:
256
256
  @function.setter
257
257
  def function(self, function):
258
258
  if function and not (
259
- isinstance(function, (str, FunctionReference)) or hasattr(function, "apply")
259
+ isinstance(function, str | FunctionReference) or hasattr(function, "apply")
260
260
  ):
261
261
  raise mlrun.errors.MLRunInvalidArgumentError(
262
262
  "function must be a uri (string) or mlrun function object/reference"
@@ -370,7 +370,7 @@ class OnlineVectorService:
370
370
  if (
371
371
  not entity_rows
372
372
  or not isinstance(entity_rows, list)
373
- or not isinstance(entity_rows[0], (list, dict))
373
+ or not isinstance(entity_rows[0], list | dict)
374
374
  ):
375
375
  raise mlrun.errors.MLRunInvalidArgumentError(
376
376
  f"input data is of type {type(entity_rows)}. must be a list of lists or list of dicts"
@@ -279,11 +279,11 @@ class MapValues(StepToDict, MLRunStep):
279
279
  new_col_type = df.schema[new_column_name].dataType
280
280
  # in order to avoid exception at isna on non-decimal/float columns -
281
281
  # we need to check their types before filtering.
282
- if isinstance(col_type, (FloatType, DoubleType, DecimalType)):
282
+ if isinstance(col_type, FloatType | DoubleType | DecimalType):
283
283
  column_filter = (~isnull(col(column))) & (~isnan(col(column)))
284
284
  else:
285
285
  column_filter = ~isnull(col(column))
286
- if isinstance(new_col_type, (FloatType, DoubleType, DecimalType)):
286
+ if isinstance(new_col_type, FloatType | DoubleType | DecimalType):
287
287
  new_column_filter = isnull(col(new_column_name)) | isnan(
288
288
  col(new_column_name)
289
289
  )
@@ -295,7 +295,7 @@ class MapValues(StepToDict, MLRunStep):
295
295
  for k, v in column_map.items()
296
296
  if v is None
297
297
  or (
298
- isinstance(v, (float, np.float64, np.float32, np.float16))
298
+ isinstance(v, float | np.float64 | np.float32 | np.float16)
299
299
  and math.isnan(v)
300
300
  )
301
301
  ]
@@ -338,7 +338,7 @@ class MapValues(StepToDict, MLRunStep):
338
338
  for val in column_map.values()
339
339
  if type(val) is not None
340
340
  and not (
341
- isinstance(val, (float, np.float64, np.float32, np.float16))
341
+ isinstance(val, float | np.float64 | np.float32 | np.float16)
342
342
  and math.isnan(val)
343
343
  )
344
344
  )
@@ -358,7 +358,9 @@ class MapValues(StepToDict, MLRunStep):
358
358
  and val != "-inf"
359
359
  and val != "inf"
360
360
  and not (
361
- isinstance(val, (float, np.float64, np.float32, np.float16))
361
+ isinstance(
362
+ val, float | np.float64 | np.float32 | np.float16
363
+ )
362
364
  and math.isnan(val)
363
365
  )
364
366
  )
@@ -443,7 +445,7 @@ class OneHotEncoder(StepToDict, MLRunStep):
443
445
  self.mapping = mapping
444
446
  for key, values in mapping.items():
445
447
  for val in values:
446
- if not (isinstance(val, str) or isinstance(val, (int, np.integer))):
448
+ if not (isinstance(val, str) or isinstance(val, int | np.integer)):
447
449
  raise mlrun.errors.MLRunInvalidArgumentError(
448
450
  "For OneHotEncoder you must provide int or string mapping list"
449
451
  )
@@ -97,9 +97,9 @@ class CommonUtils(ABC):
97
97
  """
98
98
  if isinstance(dataset, np.ndarray):
99
99
  return dataset
100
- if isinstance(dataset, (pd.DataFrame, pd.Series)):
100
+ if isinstance(dataset, pd.DataFrame | pd.Series):
101
101
  return dataset.to_numpy()
102
- if isinstance(dataset, (list, tuple)):
102
+ if isinstance(dataset, list | tuple):
103
103
  return np.array(dataset)
104
104
  if isinstance(dataset, dict):
105
105
  return np.array(list(dataset.values()))
@@ -133,7 +133,7 @@ class CommonUtils(ABC):
133
133
  """
134
134
  if isinstance(dataset, pd.DataFrame):
135
135
  return dataset
136
- if isinstance(dataset, (np.ndarray, pd.Series, list, tuple, dict)):
136
+ if isinstance(dataset, np.ndarray | pd.Series | list | tuple | dict):
137
137
  return pd.DataFrame(dataset)
138
138
  try:
139
139
  # SciPy is not in MLRun's requirements but common to all frameworks.
@@ -281,7 +281,7 @@ class Logger:
281
281
  """
282
282
  for parameter_name, parameter_value in self._context.parameters.items():
283
283
  # Check if the parameter is a trackable value:
284
- if isinstance(parameter_value, (str, bool, float, int)):
284
+ if isinstance(parameter_value, str | bool | float | int):
285
285
  self.log_static_hyperparameter(
286
286
  parameter_name=parameter_name, value=parameter_value
287
287
  )
@@ -14,8 +14,9 @@
14
14
 
15
15
  import os
16
16
  from abc import abstractmethod
17
+ from collections.abc import Callable
17
18
  from datetime import datetime
18
- from typing import Any, Callable, Generic, Optional, Union
19
+ from typing import Any, Generic, Optional, Union
19
20
 
20
21
  import yaml
21
22
 
@@ -73,7 +73,7 @@ class MLRunLogger(Logger):
73
73
  """
74
74
  for parameter_name, parameter_value in self._context.parameters.items():
75
75
  # Check if the parameter is a trackable value:
76
- if isinstance(parameter_value, (str, bool, float, int)):
76
+ if isinstance(parameter_value, str | bool | float | int):
77
77
  self.log_static_hyperparameter(
78
78
  parameter_name=parameter_name, value=parameter_value
79
79
  )
@@ -13,8 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from abc import ABC
16
+ from collections.abc import Callable
16
17
  from enum import Enum
17
- from typing import Callable, Union
18
+ from typing import Union
18
19
 
19
20
  import pandas as pd
20
21
  from sklearn.base import is_classifier, is_regressor
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Optional, Union
15
+ from collections.abc import Callable
16
+ from typing import Optional, Union
16
17
 
17
18
  import mlrun
18
19
  from mlrun.artifacts import get_model
@@ -58,7 +59,7 @@ def get_framework_by_instance(model: CommonTypes.ModelType) -> str:
58
59
 
59
60
  from mlrun.frameworks.xgboost import XGBoostModelHandler
60
61
 
61
- if isinstance(model, (XGBModel, Booster)):
62
+ if isinstance(model, XGBModel | Booster):
62
63
  return XGBoostModelHandler.FRAMEWORK_NAME
63
64
  except ModuleNotFoundError:
64
65
  pass
@@ -69,7 +70,7 @@ def get_framework_by_instance(model: CommonTypes.ModelType) -> str:
69
70
 
70
71
  from mlrun.frameworks.lgbm import LGBMModelHandler
71
72
 
72
- if isinstance(model, (LGBMModel, Booster)):
73
+ if isinstance(model, LGBMModel | Booster):
73
74
  return LGBMModelHandler.FRAMEWORK_NAME
74
75
  except ModuleNotFoundError:
75
76
  pass
@@ -13,8 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from abc import ABC
16
+ from collections.abc import Callable
16
17
  from types import ModuleType
17
- from typing import Callable, Optional, Union
18
+ from typing import Optional, Union
18
19
 
19
20
  import lightgbm as lgb
20
21
 
@@ -13,7 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import math
16
- from typing import Callable, Optional, Union
16
+ from collections.abc import Callable
17
+ from typing import Optional, Union
17
18
 
18
19
  import numpy as np
19
20
 
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Optional
15
+ from collections.abc import Callable
16
+ from typing import Optional
16
17
 
17
18
  import numpy as np
18
19
  import onnx
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Optional, Union
15
+ from collections.abc import Callable
16
+ from typing import Optional, Union
16
17
 
17
18
  import numpy as np
18
19
  from torch import Tensor
@@ -496,7 +497,7 @@ class LoggingCallback(Callback):
496
497
  value = self._objects[source]
497
498
  for key in key_chain:
498
499
  try:
499
- if isinstance(value, (dict, list, tuple)):
500
+ if isinstance(value, dict | list | tuple):
500
501
  value = value[key]
501
502
  else:
502
503
  value = getattr(value, key)
@@ -507,7 +508,7 @@ class LoggingCallback(Callback):
507
508
  )
508
509
 
509
510
  # Parse the value:
510
- if isinstance(value, (Tensor, Parameter)):
511
+ if isinstance(value, Tensor | Parameter):
511
512
  if value.numel() == 1:
512
513
  value = float(value)
513
514
  else:
@@ -523,7 +524,7 @@ class LoggingCallback(Callback):
523
524
  f"The parameter with the following key chain: {key_chain} is a numpy.ndarray with {value.size} "
524
525
  f"elements. numpy arrays are trackable only if they have 1 element."
525
526
  )
526
- elif not isinstance(value, (float, int, str, bool)):
527
+ elif not isinstance(value, float | int | str | bool):
527
528
  raise mlrun.errors.MLRunInvalidArgumentError(
528
529
  f"The parameter with the following key chain: {key_chain} is of type '{type(value)}'. "
529
530
  f"The only trackable types are: float, int, str and bool."
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Optional, Union
15
+ from collections.abc import Callable
16
+ from typing import Optional, Union
16
17
 
17
18
  import torch
18
19
  from torch import Tensor
@@ -12,8 +12,9 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from collections.abc import Callable
15
16
  from datetime import datetime
16
- from typing import Callable, Optional, Union
17
+ from typing import Optional, Union
17
18
 
18
19
  import torch
19
20
  from torch import Tensor
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Union
15
+ from collections.abc import Callable
16
+ from typing import Union
16
17
 
17
18
  import numpy as np
18
19
  import torch
@@ -15,7 +15,8 @@
15
15
  import importlib
16
16
  import json
17
17
  import sys
18
- from typing import Callable, Optional, Union
18
+ from collections.abc import Callable
19
+ from typing import Optional, Union
19
20
 
20
21
  import mlrun.errors
21
22
 
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Optional, Union
15
+ from collections.abc import Callable
16
+ from typing import Optional, Union
16
17
 
17
18
  import numpy as np
18
19
  import tensorflow as tf
@@ -441,8 +442,8 @@ class LoggingCallback(Callback):
441
442
  )
442
443
 
443
444
  # Parse the value:
444
- if isinstance(value, (tf.Tensor, tf.Variable)) or (
445
- is_keras_3() and isinstance(value, (keras.KerasTensor, keras.Variable))
445
+ if isinstance(value, tf.Tensor | tf.Variable) or (
446
+ is_keras_3() and isinstance(value, keras.KerasTensor | keras.Variable)
446
447
  ):
447
448
  if int(tf.size(value)) == 1:
448
449
  value = float(value)
@@ -459,7 +460,7 @@ class LoggingCallback(Callback):
459
460
  f"The parameter with the following key chain: {key_chain} is a numpy.ndarray with {value.size} "
460
461
  f"elements. numpy arrays are trackable only if they have 1 element."
461
462
  )
462
- elif not (isinstance(value, (float, int, str, bool))):
463
+ elif not (isinstance(value, float | int | str | bool)):
463
464
  raise mlrun.errors.MLRunInvalidArgumentError(
464
465
  f"The parameter with the following key chain: {key_chain} is of type '{type(value)}'. The only "
465
466
  f"trackable types are: float, int, str and bool."
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Callable, Optional, Union
15
+ from collections.abc import Callable
16
+ from typing import Optional, Union
16
17
 
17
18
  import mlrun
18
19
  from mlrun.artifacts import Artifact
@@ -12,8 +12,9 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from collections.abc import Callable
15
16
  from datetime import datetime
16
- from typing import Callable, Optional, Union
17
+ from typing import Optional, Union
17
18
 
18
19
  import tensorflow as tf
19
20
  from packaging import version
mlrun/hub/__init__.py CHANGED
@@ -11,5 +11,42 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ from typing import Optional
15
+
16
+ import mlrun
17
+ from mlrun.common.schemas.hub import HubSourceType
14
18
 
15
19
  from .module import get_hub_module, import_module
20
+ from .step import get_hub_step
21
+
22
+
23
+ def get_hub_item(
24
+ source_name: str,
25
+ item_name: str,
26
+ version: Optional[str] = None,
27
+ tag: Optional[str] = "latest",
28
+ force_refresh: bool = False,
29
+ item_type: HubSourceType = HubSourceType.functions,
30
+ ) -> mlrun.common.schemas.hub.HubItem:
31
+ """
32
+ Retrieve a specific hub item.
33
+
34
+ :param source_name: Name of source.
35
+ :param item_name: Name of the item to retrieve, as it appears in the hub catalog.
36
+ :param version: Get a specific version of the item. Default is ``None``.
37
+ :param tag: Get a specific version of the item identified by tag. Default is ``latest``.
38
+ :param force_refresh: Make the server fetch the information from the actual hub
39
+ source, rather than
40
+ rely on cached information. Default is ``False``.
41
+ :param item_type: The type of item to retrieve from the hub source (e.g: functions, modules).
42
+ :returns: :py:class:`~mlrun.common.schemas.hub.HubItem`.
43
+ """
44
+ db = mlrun.get_run_db()
45
+ return db.get_hub_item(
46
+ source_name=source_name,
47
+ item_name=item_name,
48
+ version=version,
49
+ tag=tag,
50
+ force_refresh=force_refresh,
51
+ item_type=item_type,
52
+ )
mlrun/hub/base.py ADDED
@@ -0,0 +1,142 @@
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 ClassVar, Optional
20
+
21
+ from mlrun.common.schemas.hub import HubSourceType
22
+ from mlrun.run import function_to_module, get_object
23
+ from mlrun.utils import logger
24
+
25
+ from ..errors import MLRunBadRequestError
26
+ from ..model import ModelObj
27
+ from ..utils import extend_hub_uri_if_needed
28
+
29
+
30
+ class HubAsset(ModelObj):
31
+ ASSET_TYPE: ClassVar[HubSourceType]
32
+
33
+ def __init__(
34
+ self,
35
+ name: str,
36
+ version: str,
37
+ description: Optional[str] = None,
38
+ categories: Optional[list] = None,
39
+ requirements: Optional[list] = None,
40
+ local_path: Optional[Path] = None,
41
+ filename: Optional[str] = None,
42
+ example: Optional[str] = None,
43
+ url: Optional[str] = None,
44
+ **kwargs,
45
+ ):
46
+ self.name: str = name
47
+ self.version: str = version
48
+ self.description: str = description or ""
49
+ self.categories: list = categories or []
50
+ self.requirements: list = requirements or []
51
+ self.local_path: Optional[Path] = local_path
52
+ self.filename: str = filename or name
53
+ self.example: str = example or ""
54
+ self.url: str = url or ""
55
+
56
+ def module(self):
57
+ """Import the code of the asset as a module."""
58
+ try:
59
+ return function_to_module(code=self.filename, workdir=self.local_path)
60
+ except Exception as e:
61
+ raise MLRunBadRequestError(
62
+ f"Failed to import module from {self.get_src_file_path()}: {e}"
63
+ )
64
+
65
+ def install_requirements(self) -> None:
66
+ """
67
+ Install pip-style requirements of the asset (e.g., ["pandas>=2.0.0", "requests==2.31.0"]).
68
+ """
69
+ if not self.requirements or len(self.requirements) == 0:
70
+ logger.info("No requirements to install.")
71
+ return
72
+ for req in self.requirements:
73
+ logger.info(f"Installing {req} ...")
74
+ try:
75
+ subprocess.run(
76
+ [sys.executable, "-m", "pip", "install", req], check=True, text=True
77
+ )
78
+ logger.info(f"Installed {req}")
79
+ except subprocess.CalledProcessError as e:
80
+ logger.error(f"Failed to install {req} (exit code {e.returncode})")
81
+
82
+ def download_files(
83
+ self,
84
+ local_path: Optional[str] = None,
85
+ download_example: bool = True,
86
+ ):
87
+ """
88
+ Download this hub asset’s files (code file and, if available and requested, an example notebook) to the target
89
+ directory specified by `local_path` (defaults to the current working directory).
90
+ This path will be used later to locate the code file when calling module().
91
+ """
92
+ self.local_path = self.verify_directory(path=local_path)
93
+ source_url, _ = extend_hub_uri_if_needed(
94
+ uri=self.url, asset_type=self.ASSET_TYPE, file=self.filename
95
+ )
96
+ self._download_object(obj_url=source_url, target_name=self.filename)
97
+ if download_example and self.example:
98
+ example_url, _ = extend_hub_uri_if_needed(
99
+ uri=self.url, asset_type=self.ASSET_TYPE, file=self.example
100
+ )
101
+ self._download_object(obj_url=example_url, target_name=self.example)
102
+
103
+ def _download_object(self, obj_url, target_name, secrets=None):
104
+ data = get_object(url=obj_url, secrets=secrets)
105
+ target_filepath = os.path.join(self.local_path, target_name)
106
+ with open(target_filepath, "wb") as f:
107
+ f.write(data)
108
+
109
+ @staticmethod
110
+ def verify_directory(path: Optional[str] = None) -> Path:
111
+ """
112
+ Validate that the given path is an existing directory.
113
+ If no path has been provided, returns current working directory.
114
+ """
115
+ if path:
116
+ path = Path(path)
117
+ if not path.exists():
118
+ raise ValueError(f"Path does not exist: {path}")
119
+ if not path.is_dir():
120
+ raise ValueError(f"Path is not a directory: {path}")
121
+ return path
122
+ return Path(os.getcwd())
123
+
124
+ def get_src_file_path(self) -> str:
125
+ """Get the full path to the asset's code file."""
126
+ if not self.local_path:
127
+ raise MLRunBadRequestError(
128
+ f"Local path not set. Call download_files() first to download the asset files, or "
129
+ f"set_local_path() with the directory containing {self.filename}"
130
+ )
131
+ src_path = Path(self.local_path) / self.filename
132
+ if not src_path.exists():
133
+ raise FileNotFoundError(
134
+ f"File {self.filename} not found in {self.local_path}. Call download_files() first to download the "
135
+ f"asset files, or set_local_path() with the directory containing {self.filename}"
136
+ )
137
+
138
+ return str(src_path)
139
+
140
+ def set_local_path(self, path: str):
141
+ """Set the local path where the asset's files are stored."""
142
+ self.local_path = self.verify_directory(path=path)