mlrun 1.7.0rc26__py3-none-any.whl → 1.7.0rc31__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 (78) hide show
  1. mlrun/__main__.py +7 -7
  2. mlrun/alerts/alert.py +13 -1
  3. mlrun/artifacts/manager.py +5 -0
  4. mlrun/common/constants.py +3 -3
  5. mlrun/common/formatters/artifact.py +1 -0
  6. mlrun/common/formatters/base.py +9 -9
  7. mlrun/common/schemas/alert.py +4 -8
  8. mlrun/common/schemas/api_gateway.py +7 -0
  9. mlrun/common/schemas/constants.py +3 -0
  10. mlrun/common/schemas/model_monitoring/__init__.py +1 -0
  11. mlrun/common/schemas/model_monitoring/constants.py +32 -13
  12. mlrun/common/schemas/model_monitoring/model_endpoints.py +0 -12
  13. mlrun/common/schemas/project.py +10 -9
  14. mlrun/common/schemas/schedule.py +1 -1
  15. mlrun/config.py +37 -11
  16. mlrun/data_types/spark.py +2 -2
  17. mlrun/data_types/to_pandas.py +48 -16
  18. mlrun/datastore/__init__.py +1 -0
  19. mlrun/datastore/azure_blob.py +2 -1
  20. mlrun/datastore/base.py +21 -13
  21. mlrun/datastore/datastore.py +7 -5
  22. mlrun/datastore/datastore_profile.py +1 -1
  23. mlrun/datastore/google_cloud_storage.py +1 -0
  24. mlrun/datastore/inmem.py +4 -1
  25. mlrun/datastore/s3.py +2 -0
  26. mlrun/datastore/snowflake_utils.py +3 -1
  27. mlrun/datastore/sources.py +40 -11
  28. mlrun/datastore/store_resources.py +2 -0
  29. mlrun/datastore/targets.py +71 -26
  30. mlrun/db/base.py +11 -0
  31. mlrun/db/httpdb.py +50 -31
  32. mlrun/db/nopdb.py +11 -1
  33. mlrun/errors.py +4 -0
  34. mlrun/execution.py +18 -10
  35. mlrun/feature_store/retrieval/spark_merger.py +4 -32
  36. mlrun/launcher/local.py +2 -2
  37. mlrun/model.py +27 -1
  38. mlrun/model_monitoring/api.py +9 -55
  39. mlrun/model_monitoring/applications/histogram_data_drift.py +4 -1
  40. mlrun/model_monitoring/controller.py +57 -73
  41. mlrun/model_monitoring/db/stores/__init__.py +21 -9
  42. mlrun/model_monitoring/db/stores/base/store.py +39 -1
  43. mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
  44. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +4 -2
  45. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +41 -80
  46. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +22 -27
  47. mlrun/model_monitoring/db/tsdb/__init__.py +19 -14
  48. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +4 -2
  49. mlrun/model_monitoring/helpers.py +15 -17
  50. mlrun/model_monitoring/writer.py +2 -7
  51. mlrun/projects/operations.py +1 -0
  52. mlrun/projects/project.py +87 -75
  53. mlrun/render.py +10 -5
  54. mlrun/run.py +7 -7
  55. mlrun/runtimes/base.py +1 -1
  56. mlrun/runtimes/daskjob.py +7 -1
  57. mlrun/runtimes/local.py +24 -7
  58. mlrun/runtimes/nuclio/function.py +20 -0
  59. mlrun/runtimes/pod.py +5 -29
  60. mlrun/serving/routers.py +75 -59
  61. mlrun/serving/server.py +1 -0
  62. mlrun/serving/v2_serving.py +8 -1
  63. mlrun/utils/helpers.py +46 -2
  64. mlrun/utils/logger.py +36 -2
  65. mlrun/utils/notifications/notification/base.py +4 -0
  66. mlrun/utils/notifications/notification/git.py +21 -0
  67. mlrun/utils/notifications/notification/slack.py +8 -0
  68. mlrun/utils/notifications/notification/webhook.py +41 -1
  69. mlrun/utils/notifications/notification_pusher.py +2 -2
  70. mlrun/utils/version/version.json +2 -2
  71. {mlrun-1.7.0rc26.dist-info → mlrun-1.7.0rc31.dist-info}/METADATA +13 -8
  72. {mlrun-1.7.0rc26.dist-info → mlrun-1.7.0rc31.dist-info}/RECORD +76 -78
  73. {mlrun-1.7.0rc26.dist-info → mlrun-1.7.0rc31.dist-info}/WHEEL +1 -1
  74. mlrun/feature_store/retrieval/conversion.py +0 -271
  75. mlrun/model_monitoring/controller_handler.py +0 -37
  76. {mlrun-1.7.0rc26.dist-info → mlrun-1.7.0rc31.dist-info}/LICENSE +0 -0
  77. {mlrun-1.7.0rc26.dist-info → mlrun-1.7.0rc31.dist-info}/entry_points.txt +0 -0
  78. {mlrun-1.7.0rc26.dist-info → mlrun-1.7.0rc31.dist-info}/top_level.txt +0 -0
@@ -59,13 +59,17 @@ def get_stream_path(
59
59
 
60
60
  stream_uri = mlrun.get_secret_or_env(
61
61
  mlrun.common.schemas.model_monitoring.ProjectSecretKeys.STREAM_PATH
62
- ) or mlrun.mlconf.get_model_monitoring_file_target_path(
63
- project=project,
64
- kind=mlrun.common.schemas.model_monitoring.FileTargetKind.STREAM,
65
- target="online",
66
- function_name=function_name,
67
62
  )
68
63
 
64
+ if not stream_uri or stream_uri == "v3io":
65
+ # TODO : remove the first part of this condition in 1.9.0
66
+ stream_uri = mlrun.mlconf.get_model_monitoring_file_target_path(
67
+ project=project,
68
+ kind=mlrun.common.schemas.model_monitoring.FileTargetKind.STREAM,
69
+ target="online",
70
+ function_name=function_name,
71
+ )
72
+
69
73
  if isinstance(stream_uri, list): # ML-6043 - user side gets only the new stream uri
70
74
  stream_uri = stream_uri[1] # get new stream path, under projects
71
75
  return mlrun.common.model_monitoring.helpers.parse_monitoring_stream_path(
@@ -107,12 +111,9 @@ def get_connection_string(secret_provider: typing.Callable[[str], str] = None) -
107
111
 
108
112
  """
109
113
 
110
- return (
111
- mlrun.get_secret_or_env(
112
- key=mlrun.common.schemas.model_monitoring.ProjectSecretKeys.ENDPOINT_STORE_CONNECTION,
113
- secret_provider=secret_provider,
114
- )
115
- or mlrun.mlconf.model_endpoint_monitoring.endpoint_store_connection
114
+ return mlrun.get_secret_or_env(
115
+ key=mlrun.common.schemas.model_monitoring.ProjectSecretKeys.ENDPOINT_STORE_CONNECTION,
116
+ secret_provider=secret_provider,
116
117
  )
117
118
 
118
119
 
@@ -125,12 +126,9 @@ def get_tsdb_connection_string(
125
126
  :return: Valid TSDB connection string.
126
127
  """
127
128
 
128
- return (
129
- mlrun.get_secret_or_env(
130
- key=mlrun.common.schemas.model_monitoring.ProjectSecretKeys.TSDB_CONNECTION,
131
- secret_provider=secret_provider,
132
- )
133
- or mlrun.mlconf.model_endpoint_monitoring.tsdb_connection
129
+ return mlrun.get_secret_or_env(
130
+ key=mlrun.common.schemas.model_monitoring.ProjectSecretKeys.TSDB_CONNECTION,
131
+ secret_provider=secret_provider,
134
132
  )
135
133
 
136
134
 
@@ -153,11 +153,7 @@ class ModelMonitoringWriter(StepToDict):
153
153
  result_kind: int, result_status: int
154
154
  ) -> alert_objects.EventKind:
155
155
  """Generate the required Event Kind format for the alerting system"""
156
- if result_kind == ResultKindApp.custom.value:
157
- # Custom kind is represented as an anomaly detection
158
- event_kind = "mm_app_anomaly"
159
- else:
160
- event_kind = ResultKindApp(value=result_kind).name
156
+ event_kind = ResultKindApp(value=result_kind).name
161
157
 
162
158
  if result_status == ResultStatusApp.detected.value:
163
159
  event_kind = f"{event_kind}_detected"
@@ -261,8 +257,7 @@ class ModelMonitoringWriter(StepToDict):
261
257
  "data drift app",
262
258
  endpoint_id=endpoint_id,
263
259
  )
264
- store = mlrun.model_monitoring.get_store_object(project=self.project)
265
- store.update_model_endpoint(
260
+ self._app_result_store.update_model_endpoint(
266
261
  endpoint_id=endpoint_id,
267
262
  attributes=json.loads(event[ResultData.RESULT_EXTRA_DATA]),
268
263
  )
@@ -330,6 +330,7 @@ def build_function(
330
330
  commands=commands,
331
331
  secret=secret_name,
332
332
  requirements=requirements,
333
+ requirements_file=requirements_file,
333
334
  overwrite=overwrite_build_params,
334
335
  extra_args=extra_args,
335
336
  )
mlrun/projects/project.py CHANGED
@@ -714,7 +714,8 @@ def _project_instance_from_struct(struct, name, allow_cross_project):
714
714
  name_from_struct = struct.get("metadata", {}).get("name", "")
715
715
  if name and name_from_struct and name_from_struct != name:
716
716
  error_message = (
717
- f"project name mismatch, {name_from_struct} != {name}, please do one of the following:\n"
717
+ f"Project name mismatch, {name_from_struct} != {name}, project is loaded from {name_from_struct} "
718
+ f"project yaml. To prevent/allow this, you can take one of the following actions:\n"
718
719
  "1. Set the `allow_cross_project=True` when loading the project.\n"
719
720
  f"2. Delete the existing project yaml, or ensure its name is equal to {name}.\n"
720
721
  "3. Use different project context dir."
@@ -722,14 +723,14 @@ def _project_instance_from_struct(struct, name, allow_cross_project):
722
723
 
723
724
  if allow_cross_project is None:
724
725
  # TODO: Remove this warning in version 1.9.0 and also fix cli to support allow_cross_project
725
- logger.warn(
726
- "Project name is different than specified on its project yaml."
727
- "You should fix it until version 1.9.0",
728
- description=error_message,
726
+ warnings.warn(
727
+ f"Project {name=} is different than specified on the context's project yaml. "
728
+ "This behavior is deprecated and will not be supported in version 1.9.0."
729
729
  )
730
+ logger.warn(error_message)
730
731
  elif allow_cross_project:
731
- logger.warn(
732
- "Project name is different than specified on its project yaml. Overriding.",
732
+ logger.debug(
733
+ "Project name is different than specified on the context's project yaml. Overriding.",
733
734
  existing_name=name_from_struct,
734
735
  overriding_name=name,
735
736
  )
@@ -1007,8 +1008,13 @@ class ProjectSpec(ModelObj):
1007
1008
  key = artifact.key
1008
1009
  artifact = artifact.to_dict()
1009
1010
  else: # artifact is a dict
1010
- # imported artifacts don't have metadata,spec,status fields
1011
- key_field = "key" if _is_imported_artifact(artifact) else "metadata.key"
1011
+ # imported/legacy artifacts don't have metadata,spec,status fields
1012
+ key_field = (
1013
+ "key"
1014
+ if _is_imported_artifact(artifact)
1015
+ or mlrun.utils.is_legacy_artifact(artifact)
1016
+ else "metadata.key"
1017
+ )
1012
1018
  key = mlrun.utils.get_in(artifact, key_field, "")
1013
1019
  if not key:
1014
1020
  raise ValueError(f'artifacts "{key_field}" must be specified')
@@ -2127,6 +2133,7 @@ class MlrunProject(ModelObj):
2127
2133
  deploy_histogram_data_drift_app: bool = True,
2128
2134
  wait_for_deployment: bool = False,
2129
2135
  rebuild_images: bool = False,
2136
+ fetch_credentials_from_sys_config: bool = False,
2130
2137
  ) -> None:
2131
2138
  """
2132
2139
  Deploy model monitoring application controller, writer and stream functions.
@@ -2136,17 +2143,18 @@ class MlrunProject(ModelObj):
2136
2143
  The stream function goal is to monitor the log of the data stream. It is triggered when a new log entry
2137
2144
  is detected. It processes the new events into statistics that are then written to statistics databases.
2138
2145
 
2139
- :param default_controller_image: Deprecated.
2140
- :param base_period: The time period in minutes in which the model monitoring controller
2141
- function is triggered. By default, the base period is 10 minutes.
2142
- :param image: The image of the model monitoring controller, writer, monitoring
2143
- stream & histogram data drift functions, which are real time nuclio
2144
- functions. By default, the image is mlrun/mlrun.
2145
- :param deploy_histogram_data_drift_app: If true, deploy the default histogram-based data drift application.
2146
- :param wait_for_deployment: If true, return only after the deployment is done on the backend.
2147
- Otherwise, deploy the model monitoring infrastructure on the
2148
- background, including the histogram data drift app if selected.
2149
- :param rebuild_images: If true, force rebuild of model monitoring infrastructure images.
2146
+ :param default_controller_image: Deprecated.
2147
+ :param base_period: The time period in minutes in which the model monitoring controller
2148
+ function is triggered. By default, the base period is 10 minutes.
2149
+ :param image: The image of the model monitoring controller, writer, monitoring
2150
+ stream & histogram data drift functions, which are real time nuclio
2151
+ functions. By default, the image is mlrun/mlrun.
2152
+ :param deploy_histogram_data_drift_app: If true, deploy the default histogram-based data drift application.
2153
+ :param wait_for_deployment: If true, return only after the deployment is done on the backend.
2154
+ Otherwise, deploy the model monitoring infrastructure on the
2155
+ background, including the histogram data drift app if selected.
2156
+ :param rebuild_images: If true, force rebuild of model monitoring infrastructure images.
2157
+ :param fetch_credentials_from_sys_config: If true, fetch the credentials from the system configuration.
2150
2158
  """
2151
2159
  if default_controller_image != "mlrun/mlrun":
2152
2160
  # TODO: Remove this in 1.9.0
@@ -2163,6 +2171,7 @@ class MlrunProject(ModelObj):
2163
2171
  base_period=base_period,
2164
2172
  deploy_histogram_data_drift_app=deploy_histogram_data_drift_app,
2165
2173
  rebuild_images=rebuild_images,
2174
+ fetch_credentials_from_sys_config=fetch_credentials_from_sys_config,
2166
2175
  )
2167
2176
 
2168
2177
  if wait_for_deployment:
@@ -2485,25 +2494,17 @@ class MlrunProject(ModelObj):
2485
2494
  self.spec.remove_function(name)
2486
2495
 
2487
2496
  def remove_model_monitoring_function(self, name: Union[str, list[str]]):
2488
- """remove the specified model-monitoring-app function/s from the project spec
2497
+ """delete the specified model-monitoring-app function/s
2489
2498
 
2490
2499
  :param name: name of the model-monitoring-function/s (under the project)
2491
2500
  """
2492
- names = name if isinstance(name, list) else [name]
2493
- for func_name in names:
2494
- function = self.get_function(key=func_name)
2495
- if (
2496
- function.metadata.labels.get(mm_constants.ModelMonitoringAppLabel.KEY)
2497
- == mm_constants.ModelMonitoringAppLabel.VAL
2498
- ):
2499
- self.remove_function(name=func_name)
2500
- logger.info(
2501
- f"{func_name} function has been removed from {self.name} project"
2502
- )
2503
- else:
2504
- raise logger.warn(
2505
- f"There is no model monitoring function with {func_name} name"
2506
- )
2501
+ # TODO: Remove this in 1.9.0
2502
+ warnings.warn(
2503
+ "'remove_model_monitoring_function' is deprecated and will be removed in 1.9.0. "
2504
+ "Please use `delete_model_monitoring_function` instead.",
2505
+ FutureWarning,
2506
+ )
2507
+ self.delete_model_monitoring_function(name)
2507
2508
 
2508
2509
  def delete_model_monitoring_function(self, name: Union[str, list[str]]):
2509
2510
  """delete the specified model-monitoring-app function/s
@@ -3204,51 +3205,62 @@ class MlrunProject(ModelObj):
3204
3205
  endpoint_store_connection: Optional[str] = None,
3205
3206
  stream_path: Optional[str] = None,
3206
3207
  tsdb_connection: Optional[str] = None,
3208
+ replace_creds: bool = False,
3207
3209
  ):
3208
- """Set the credentials that will be used by the project's model monitoring
3210
+ """
3211
+ Set the credentials that will be used by the project's model monitoring
3209
3212
  infrastructure functions. Important to note that you have to set the credentials before deploying any
3210
3213
  model monitoring or serving function.
3211
3214
 
3212
- :param access_key: Model Monitoring access key for managing user permissions
3213
- :param endpoint_store_connection: Endpoint store connection string
3214
- :param stream_path: Path to the model monitoring stream
3215
- :param tsdb_connection: Connection string to the time series database
3215
+ :param access_key: Model Monitoring access key for managing user permissions.
3216
+ :param endpoint_store_connection: Endpoint store connection string. By default, None.
3217
+ Options:
3218
+ 1. None, will be set from the system configuration.
3219
+ 2. v3io - for v3io endpoint store,
3220
+ pass `v3io` and the system will generate the exact path.
3221
+ 3. MySQL/SQLite - for SQL endpoint store, please provide full
3222
+ connection string, for example
3223
+ mysql+pymysql://<username>:<password>@<host>:<port>/<db_name>
3224
+ :param stream_path: Path to the model monitoring stream. By default, None.
3225
+ Options:
3226
+ 1. None, will be set from the system configuration.
3227
+ 2. v3io - for v3io stream,
3228
+ pass `v3io` and the system will generate the exact path.
3229
+ 3. Kafka - for Kafka stream, please provide full connection string without
3230
+ custom topic, for example kafka://<some_kafka_broker>:<port>.
3231
+ :param tsdb_connection: Connection string to the time series database. By default, None.
3232
+ Options:
3233
+ 1. None, will be set from the system configuration.
3234
+ 2. v3io - for v3io stream,
3235
+ pass `v3io` and the system will generate the exact path.
3236
+ 3. TDEngine - for TDEngine tsdb, please provide full websocket connection URL,
3237
+ for example taosws://<username>:<password>@<host>:<port>.
3238
+ :param replace_creds: If True, will override the existing credentials.
3239
+ Please keep in mind that if you already enabled model monitoring on
3240
+ your project this action can cause data loose and will require redeploying
3241
+ all model monitoring functions & model monitoring infra
3242
+ & tracked model server.
3216
3243
  """
3217
-
3218
- secrets_dict = {}
3219
- if access_key:
3220
- secrets_dict[
3221
- mlrun.common.schemas.model_monitoring.ProjectSecretKeys.ACCESS_KEY
3222
- ] = access_key
3223
-
3224
- if endpoint_store_connection:
3225
- secrets_dict[
3226
- mlrun.common.schemas.model_monitoring.ProjectSecretKeys.ENDPOINT_STORE_CONNECTION
3227
- ] = endpoint_store_connection
3228
-
3229
- if stream_path:
3230
- if stream_path.startswith("kafka://") and "?topic" in stream_path:
3231
- raise mlrun.errors.MLRunInvalidArgumentError(
3232
- "Custom kafka topic is not allowed"
3233
- )
3234
- secrets_dict[
3235
- mlrun.common.schemas.model_monitoring.ProjectSecretKeys.STREAM_PATH
3236
- ] = stream_path
3237
-
3238
- if tsdb_connection:
3239
- if not tsdb_connection.startswith("taosws://"):
3240
- raise mlrun.errors.MLRunInvalidArgumentError(
3241
- "Currently only TDEngine websocket connection is supported for non-v3io TSDB,"
3242
- "please provide a full URL (e.g. taosws://user:password@host:port)"
3243
- )
3244
- secrets_dict[
3245
- mlrun.common.schemas.model_monitoring.ProjectSecretKeys.TSDB_CONNECTION
3246
- ] = tsdb_connection
3247
-
3248
- self.set_secrets(
3249
- secrets=secrets_dict,
3250
- provider=mlrun.common.schemas.SecretProviderName.kubernetes,
3244
+ db = mlrun.db.get_run_db(secrets=self._secrets)
3245
+ db.set_model_monitoring_credentials(
3246
+ project=self.name,
3247
+ credentials={
3248
+ "access_key": access_key,
3249
+ "endpoint_store_connection": endpoint_store_connection,
3250
+ "stream_path": stream_path,
3251
+ "tsdb_connection": tsdb_connection,
3252
+ },
3253
+ replace_creds=replace_creds,
3251
3254
  )
3255
+ if replace_creds:
3256
+ logger.info(
3257
+ "Model monitoring credentials were set successfully. "
3258
+ "Please keep in mind that if you already had model monitoring functions "
3259
+ "/ model monitoring infra / tracked model server "
3260
+ "deployed on your project, you will need to redeploy them."
3261
+ "For redeploying the model monitoring infra, please use `enable_model_monitoring` API "
3262
+ "and set `rebuild_images=True`"
3263
+ )
3252
3264
 
3253
3265
  def run_function(
3254
3266
  self,
mlrun/render.py CHANGED
@@ -283,9 +283,14 @@ function copyToClipboard(fld) {
283
283
  }
284
284
  function expandPanel(el) {
285
285
  const panelName = "#" + el.getAttribute('paneName');
286
- console.log(el.title);
287
286
 
288
- document.querySelector(panelName + "-title").innerHTML = el.title
287
+ // Get the base URL of the current notebook
288
+ var baseUrl = window.location.origin;
289
+
290
+ // Construct the full URL
291
+ var fullUrl = new URL(el.title, baseUrl).href;
292
+
293
+ document.querySelector(panelName + "-title").innerHTML = fullUrl
289
294
  iframe = document.querySelector(panelName + "-body");
290
295
 
291
296
  const tblcss = `<style> body { font-family: Arial, Helvetica, sans-serif;}
@@ -299,7 +304,7 @@ function expandPanel(el) {
299
304
  }
300
305
 
301
306
  function reqListener () {
302
- if (el.title.endsWith(".csv")) {
307
+ if (fullUrl.endsWith(".csv")) {
303
308
  iframe.setAttribute("srcdoc", tblcss + csvToHtmlTable(this.responseText));
304
309
  } else {
305
310
  iframe.setAttribute("srcdoc", this.responseText);
@@ -309,11 +314,11 @@ function expandPanel(el) {
309
314
 
310
315
  const oReq = new XMLHttpRequest();
311
316
  oReq.addEventListener("load", reqListener);
312
- oReq.open("GET", el.title);
317
+ oReq.open("GET", fullUrl);
313
318
  oReq.send();
314
319
 
315
320
 
316
- //iframe.src = el.title;
321
+ //iframe.src = fullUrl;
317
322
  const resultPane = document.querySelector(panelName + "-pane");
318
323
  if (resultPane.classList.contains("hidden")) {
319
324
  resultPane.classList.remove("hidden");
mlrun/run.py CHANGED
@@ -63,11 +63,11 @@ from .runtimes.funcdoc import update_function_entry_points
63
63
  from .runtimes.nuclio.application import ApplicationRuntime
64
64
  from .runtimes.utils import add_code_metadata, global_context
65
65
  from .utils import (
66
+ RunKeys,
66
67
  extend_hub_uri_if_needed,
67
68
  get_in,
68
69
  logger,
69
70
  retry_until_successful,
70
- run_keys,
71
71
  update_in,
72
72
  )
73
73
 
@@ -201,8 +201,8 @@ def get_or_create_ctx(
201
201
  rundb: str = "",
202
202
  project: str = "",
203
203
  upload_artifacts=False,
204
- labels: dict = None,
205
- ):
204
+ labels: Optional[dict] = None,
205
+ ) -> MLClientCtx:
206
206
  """called from within the user program to obtain a run context
207
207
 
208
208
  the run context is an interface for receiving parameters, data and logging
@@ -217,10 +217,10 @@ def get_or_create_ctx(
217
217
  :param spec: dictionary holding run spec
218
218
  :param with_env: look for context in environment vars, default True
219
219
  :param rundb: path/url to the metadata and artifact database
220
- :param project: project to initiate the context in (by default mlrun.mlctx.default_project)
220
+ :param project: project to initiate the context in (by default `mlrun.mlconf.default_project`)
221
221
  :param upload_artifacts: when using local context (not as part of a job/run), upload artifacts to the
222
222
  system default artifact path location
223
- :param labels: dict of the context labels
223
+ :param labels: dict of the context labels
224
224
  :return: execution context
225
225
 
226
226
  Examples::
@@ -280,7 +280,7 @@ def get_or_create_ctx(
280
280
  artifact_path = mlrun.utils.helpers.template_artifact_path(
281
281
  mlconf.artifact_path, project or mlconf.default_project
282
282
  )
283
- update_in(newspec, ["spec", run_keys.output_path], artifact_path)
283
+ update_in(newspec, ["spec", RunKeys.output_path], artifact_path)
284
284
 
285
285
  newspec.setdefault("metadata", {})
286
286
  update_in(newspec, "metadata.name", name, replace=False)
@@ -639,7 +639,7 @@ def code_to_function(
639
639
  :param requirements: a list of python packages
640
640
  :param requirements_file: path to a python requirements file
641
641
  :param categories: list of categories for mlrun Function Hub, defaults to None
642
- :param labels: immutable name/value pairs to tag the function with useful metadata, defaults to None
642
+ :param labels: name/value pairs dict to tag the function with useful metadata, defaults to None
643
643
  :param with_doc: indicates whether to document the function parameters, defaults to True
644
644
  :param ignored_tags: notebook cells to ignore when converting notebooks to py code (separated by ';')
645
645
 
mlrun/runtimes/base.py CHANGED
@@ -674,7 +674,7 @@ class BaseRuntime(ModelObj):
674
674
  selector="",
675
675
  hyper_param_options: HyperParamOptions = None,
676
676
  inputs: dict = None,
677
- outputs: dict = None,
677
+ outputs: list = None,
678
678
  workdir: str = "",
679
679
  artifact_path: str = "",
680
680
  image: str = "",
mlrun/runtimes/daskjob.py CHANGED
@@ -548,7 +548,13 @@ class DaskCluster(KubejobRuntime):
548
548
  "specified handler (string) without command "
549
549
  "(py file path), specify command or use handler pointer"
550
550
  )
551
- handler = load_module(self.spec.command, handler, context=context)
551
+ # Do not embed the module in system as it is not persistent with the dask cluster
552
+ handler = load_module(
553
+ self.spec.command,
554
+ handler,
555
+ context=context,
556
+ embed_in_sys=False,
557
+ )
552
558
  client = self.client
553
559
  setattr(context, "dask_client", client)
554
560
  sout, serr = exec_from_params(handler, runobj, context)
mlrun/runtimes/local.py CHANGED
@@ -58,7 +58,9 @@ class ParallelRunner:
58
58
 
59
59
  return TrackerManager()
60
60
 
61
- def _get_handler(self, handler, context):
61
+ def _get_handler(
62
+ self, handler: str, context: MLClientCtx, embed_in_sys: bool = True
63
+ ):
62
64
  return handler
63
65
 
64
66
  def _get_dask_client(self, options):
@@ -86,7 +88,7 @@ class ParallelRunner:
86
88
  handler = runobj.spec.handler
87
89
  self._force_handler(handler)
88
90
  set_paths(self.spec.pythonpath)
89
- handler = self._get_handler(handler, execution)
91
+ handler = self._get_handler(handler, execution, embed_in_sys=False)
90
92
 
91
93
  client, function_name = self._get_dask_client(generator.options)
92
94
  parallel_runs = generator.options.parallel_runs or 4
@@ -224,12 +226,14 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
224
226
  def is_deployed(self):
225
227
  return True
226
228
 
227
- def _get_handler(self, handler, context):
229
+ def _get_handler(
230
+ self, handler: str, context: MLClientCtx, embed_in_sys: bool = True
231
+ ):
228
232
  command = self.spec.command
229
233
  if not command and self.spec.build.functionSourceCode:
230
234
  # if the code is embedded in the function object extract or find it
231
235
  command, _ = mlrun.run.load_func_code(self)
232
- return load_module(command, handler, context)
236
+ return load_module(command, handler, context, embed_in_sys=embed_in_sys)
233
237
 
234
238
  def _pre_run(self, runobj: RunObject, execution: MLClientCtx):
235
239
  workdir = self.spec.workdir
@@ -372,8 +376,20 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
372
376
  return run_obj_dict
373
377
 
374
378
 
375
- def load_module(file_name, handler, context):
376
- """Load module from file name"""
379
+ def load_module(
380
+ file_name: str,
381
+ handler: str,
382
+ context: MLClientCtx,
383
+ embed_in_sys: bool = True,
384
+ ):
385
+ """
386
+ Load module from filename
387
+ :param file_name: The module path to load
388
+ :param handler: The callable to load
389
+ :param context: Execution context
390
+ :param embed_in_sys: Embed the file-named module in sys.modules. This is not persistent with remote
391
+ environments and therefore can effect pickling.
392
+ """
377
393
  module = None
378
394
  if file_name:
379
395
  path = Path(file_name)
@@ -384,7 +400,8 @@ def load_module(file_name, handler, context):
384
400
  if spec is None:
385
401
  raise RunError(f"Cannot import from {file_name!r}")
386
402
  module = imputil.module_from_spec(spec)
387
- sys.modules[mod_name] = module
403
+ if embed_in_sys:
404
+ sys.modules[mod_name] = module
388
405
  spec.loader.exec_module(module)
389
406
 
390
407
  class_args = {}
@@ -1327,3 +1327,23 @@ def get_nuclio_deploy_status(
1327
1327
  else:
1328
1328
  text = "\n".join(outputs) if outputs else ""
1329
1329
  return state, address, name, last_log_timestamp, text, function_status
1330
+
1331
+
1332
+ def enrich_nuclio_function_from_headers(
1333
+ func: RemoteRuntime,
1334
+ headers: dict,
1335
+ ):
1336
+ func.status.state = headers.get("x-mlrun-function-status", "")
1337
+ func.status.address = headers.get("x-mlrun-address", "")
1338
+ func.status.nuclio_name = headers.get("x-mlrun-name", "")
1339
+ func.status.internal_invocation_urls = (
1340
+ headers.get("x-mlrun-internal-invocation-urls", "").split(",")
1341
+ if headers.get("x-mlrun-internal-invocation-urls")
1342
+ else []
1343
+ )
1344
+ func.status.external_invocation_urls = (
1345
+ headers.get("x-mlrun-external-invocation-urls", "").split(",")
1346
+ if headers.get("x-mlrun-external-invocation-urls")
1347
+ else []
1348
+ )
1349
+ func.status.container_image = headers.get("x-mlrun-container-image", "")
mlrun/runtimes/pod.py CHANGED
@@ -532,7 +532,9 @@ class KubeResourceSpec(FunctionSpec):
532
532
  return
533
533
 
534
534
  # merge node selectors - precedence to existing node selector
535
- self.node_selector = {**node_selector, **self.node_selector}
535
+ self.node_selector = mlrun.utils.helpers.merge_with_precedence(
536
+ node_selector, self.node_selector
537
+ )
536
538
 
537
539
  def _merge_tolerations(
538
540
  self,
@@ -1038,32 +1040,6 @@ class KubeResource(BaseRuntime, KfpAdapterMixin):
1038
1040
  return True
1039
1041
  return False
1040
1042
 
1041
- def enrich_runtime_spec(
1042
- self,
1043
- project_node_selector: dict[str, str],
1044
- ):
1045
- """
1046
- Enriches the runtime spec with the project-level node selector.
1047
-
1048
- This method merges the project-level node selector with the existing function node_selector.
1049
- The merge logic used here combines the two dictionaries, giving precedence to
1050
- the keys in the runtime node_selector. If there are conflicting keys between the
1051
- two dictionaries, the values from self.spec.node_selector will overwrite the
1052
- values from project_node_selector.
1053
-
1054
- Example:
1055
- Suppose self.spec.node_selector = {"type": "gpu", "zone": "us-east-1"}
1056
- and project_node_selector = {"type": "cpu", "environment": "production"}.
1057
- After the merge, the resulting node_selector will be:
1058
- {"type": "gpu", "zone": "us-east-1", "environment": "production"}
1059
-
1060
- Note:
1061
- - The merge uses the ** operator, also known as the "unpacking" operator in Python,
1062
- combining key-value pairs from each dictionary. Later dictionaries take precedence
1063
- when there are conflicting keys.
1064
- """
1065
- self.spec.node_selector = {**project_node_selector, **self.spec.node_selector}
1066
-
1067
1043
  def _set_env(self, name, value=None, value_from=None):
1068
1044
  new_var = k8s_client.V1EnvVar(name=name, value=value, value_from=value_from)
1069
1045
 
@@ -1542,7 +1518,7 @@ def get_sanitized_attribute(spec, attribute_name: str):
1542
1518
 
1543
1519
  # check if attribute of type dict, and then check if type is sanitized
1544
1520
  if isinstance(attribute, dict):
1545
- if attribute_config["not_sanitized_class"] != dict:
1521
+ if not isinstance(attribute_config["not_sanitized_class"], dict):
1546
1522
  raise mlrun.errors.MLRunInvalidArgumentTypeError(
1547
1523
  f"expected to be of type {attribute_config.get('not_sanitized_class')} but got dict"
1548
1524
  )
@@ -1552,7 +1528,7 @@ def get_sanitized_attribute(spec, attribute_name: str):
1552
1528
  elif isinstance(attribute, list) and not isinstance(
1553
1529
  attribute[0], attribute_config["sub_attribute_type"]
1554
1530
  ):
1555
- if attribute_config["not_sanitized_class"] != list:
1531
+ if not isinstance(attribute_config["not_sanitized_class"], list):
1556
1532
  raise mlrun.errors.MLRunInvalidArgumentTypeError(
1557
1533
  f"expected to be of type {attribute_config.get('not_sanitized_class')} but got list"
1558
1534
  )