mlrun 1.7.0rc5__py3-none-any.whl → 1.7.2__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 (234) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +39 -121
  3. mlrun/{datastore/helpers.py → alerts/__init__.py} +2 -5
  4. mlrun/alerts/alert.py +248 -0
  5. mlrun/api/schemas/__init__.py +4 -3
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +39 -254
  8. mlrun/artifacts/dataset.py +9 -190
  9. mlrun/artifacts/manager.py +73 -46
  10. mlrun/artifacts/model.py +30 -158
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +73 -2
  13. mlrun/common/db/sql_session.py +3 -2
  14. mlrun/common/formatters/__init__.py +21 -0
  15. mlrun/common/formatters/artifact.py +46 -0
  16. mlrun/common/formatters/base.py +113 -0
  17. mlrun/common/formatters/feature_set.py +44 -0
  18. mlrun/common/formatters/function.py +46 -0
  19. mlrun/common/formatters/pipeline.py +53 -0
  20. mlrun/common/formatters/project.py +51 -0
  21. mlrun/common/formatters/run.py +29 -0
  22. mlrun/common/helpers.py +11 -1
  23. mlrun/{runtimes → common/runtimes}/constants.py +32 -4
  24. mlrun/common/schemas/__init__.py +21 -4
  25. mlrun/common/schemas/alert.py +202 -0
  26. mlrun/common/schemas/api_gateway.py +113 -2
  27. mlrun/common/schemas/artifact.py +28 -1
  28. mlrun/common/schemas/auth.py +11 -0
  29. mlrun/common/schemas/client_spec.py +2 -1
  30. mlrun/common/schemas/common.py +7 -4
  31. mlrun/common/schemas/constants.py +3 -0
  32. mlrun/common/schemas/feature_store.py +58 -28
  33. mlrun/common/schemas/frontend_spec.py +8 -0
  34. mlrun/common/schemas/function.py +11 -0
  35. mlrun/common/schemas/hub.py +7 -9
  36. mlrun/common/schemas/model_monitoring/__init__.py +21 -4
  37. mlrun/common/schemas/model_monitoring/constants.py +136 -42
  38. mlrun/common/schemas/model_monitoring/grafana.py +9 -5
  39. mlrun/common/schemas/model_monitoring/model_endpoints.py +89 -41
  40. mlrun/common/schemas/notification.py +69 -12
  41. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  42. mlrun/common/schemas/pipeline.py +7 -0
  43. mlrun/common/schemas/project.py +67 -16
  44. mlrun/common/schemas/runs.py +17 -0
  45. mlrun/common/schemas/schedule.py +1 -1
  46. mlrun/common/schemas/workflow.py +10 -2
  47. mlrun/common/types.py +14 -1
  48. mlrun/config.py +224 -58
  49. mlrun/data_types/data_types.py +11 -1
  50. mlrun/data_types/spark.py +5 -4
  51. mlrun/data_types/to_pandas.py +75 -34
  52. mlrun/datastore/__init__.py +8 -10
  53. mlrun/datastore/alibaba_oss.py +131 -0
  54. mlrun/datastore/azure_blob.py +131 -43
  55. mlrun/datastore/base.py +107 -47
  56. mlrun/datastore/datastore.py +17 -7
  57. mlrun/datastore/datastore_profile.py +91 -7
  58. mlrun/datastore/dbfs_store.py +3 -7
  59. mlrun/datastore/filestore.py +1 -3
  60. mlrun/datastore/google_cloud_storage.py +92 -32
  61. mlrun/datastore/hdfs.py +5 -0
  62. mlrun/datastore/inmem.py +6 -3
  63. mlrun/datastore/redis.py +3 -2
  64. mlrun/datastore/s3.py +30 -12
  65. mlrun/datastore/snowflake_utils.py +45 -0
  66. mlrun/datastore/sources.py +274 -59
  67. mlrun/datastore/spark_utils.py +30 -0
  68. mlrun/datastore/store_resources.py +9 -7
  69. mlrun/datastore/storeytargets.py +151 -0
  70. mlrun/datastore/targets.py +374 -102
  71. mlrun/datastore/utils.py +68 -5
  72. mlrun/datastore/v3io.py +28 -50
  73. mlrun/db/auth_utils.py +152 -0
  74. mlrun/db/base.py +231 -22
  75. mlrun/db/factory.py +1 -4
  76. mlrun/db/httpdb.py +864 -228
  77. mlrun/db/nopdb.py +268 -16
  78. mlrun/errors.py +35 -5
  79. mlrun/execution.py +111 -38
  80. mlrun/feature_store/__init__.py +0 -2
  81. mlrun/feature_store/api.py +46 -53
  82. mlrun/feature_store/common.py +6 -11
  83. mlrun/feature_store/feature_set.py +48 -23
  84. mlrun/feature_store/feature_vector.py +13 -2
  85. mlrun/feature_store/ingestion.py +7 -6
  86. mlrun/feature_store/retrieval/base.py +9 -4
  87. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  88. mlrun/feature_store/retrieval/job.py +13 -4
  89. mlrun/feature_store/retrieval/local_merger.py +2 -0
  90. mlrun/feature_store/retrieval/spark_merger.py +24 -32
  91. mlrun/feature_store/steps.py +38 -19
  92. mlrun/features.py +6 -14
  93. mlrun/frameworks/_common/plan.py +3 -3
  94. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
  95. mlrun/frameworks/_ml_common/plan.py +1 -1
  96. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  97. mlrun/frameworks/lgbm/__init__.py +1 -1
  98. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  99. mlrun/frameworks/lgbm/model_handler.py +1 -1
  100. mlrun/frameworks/parallel_coordinates.py +4 -4
  101. mlrun/frameworks/pytorch/__init__.py +2 -2
  102. mlrun/frameworks/sklearn/__init__.py +1 -1
  103. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  104. mlrun/frameworks/tf_keras/__init__.py +5 -2
  105. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  106. mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
  107. mlrun/frameworks/xgboost/__init__.py +1 -1
  108. mlrun/k8s_utils.py +57 -12
  109. mlrun/launcher/__init__.py +1 -1
  110. mlrun/launcher/base.py +6 -5
  111. mlrun/launcher/client.py +13 -11
  112. mlrun/launcher/factory.py +1 -1
  113. mlrun/launcher/local.py +15 -5
  114. mlrun/launcher/remote.py +10 -3
  115. mlrun/lists.py +6 -2
  116. mlrun/model.py +297 -48
  117. mlrun/model_monitoring/__init__.py +1 -1
  118. mlrun/model_monitoring/api.py +152 -357
  119. mlrun/model_monitoring/applications/__init__.py +10 -0
  120. mlrun/model_monitoring/applications/_application_steps.py +190 -0
  121. mlrun/model_monitoring/applications/base.py +108 -0
  122. mlrun/model_monitoring/applications/context.py +341 -0
  123. mlrun/model_monitoring/{evidently_application.py → applications/evidently_base.py} +27 -22
  124. mlrun/model_monitoring/applications/histogram_data_drift.py +227 -91
  125. mlrun/model_monitoring/applications/results.py +99 -0
  126. mlrun/model_monitoring/controller.py +130 -303
  127. mlrun/model_monitoring/{stores/models/sqlite.py → db/__init__.py} +5 -10
  128. mlrun/model_monitoring/db/stores/__init__.py +136 -0
  129. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  130. mlrun/model_monitoring/db/stores/base/store.py +213 -0
  131. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  132. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  133. mlrun/model_monitoring/db/stores/sqldb/models/base.py +190 -0
  134. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +103 -0
  135. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  136. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +659 -0
  137. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  138. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +726 -0
  139. mlrun/model_monitoring/db/tsdb/__init__.py +105 -0
  140. mlrun/model_monitoring/db/tsdb/base.py +448 -0
  141. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  142. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  143. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +298 -0
  144. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +42 -0
  145. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +522 -0
  146. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  147. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +158 -0
  148. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +849 -0
  149. mlrun/model_monitoring/features_drift_table.py +34 -22
  150. mlrun/model_monitoring/helpers.py +177 -39
  151. mlrun/model_monitoring/model_endpoint.py +3 -2
  152. mlrun/model_monitoring/stream_processing.py +165 -398
  153. mlrun/model_monitoring/tracking_policy.py +7 -1
  154. mlrun/model_monitoring/writer.py +161 -125
  155. mlrun/package/packagers/default_packager.py +2 -2
  156. mlrun/package/packagers_manager.py +1 -0
  157. mlrun/package/utils/_formatter.py +2 -2
  158. mlrun/platforms/__init__.py +11 -10
  159. mlrun/platforms/iguazio.py +67 -228
  160. mlrun/projects/__init__.py +6 -1
  161. mlrun/projects/operations.py +47 -20
  162. mlrun/projects/pipelines.py +396 -249
  163. mlrun/projects/project.py +1125 -414
  164. mlrun/render.py +28 -22
  165. mlrun/run.py +207 -180
  166. mlrun/runtimes/__init__.py +76 -11
  167. mlrun/runtimes/base.py +40 -14
  168. mlrun/runtimes/daskjob.py +9 -2
  169. mlrun/runtimes/databricks_job/databricks_runtime.py +1 -0
  170. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  171. mlrun/runtimes/funcdoc.py +1 -29
  172. mlrun/runtimes/kubejob.py +34 -128
  173. mlrun/runtimes/local.py +39 -10
  174. mlrun/runtimes/mpijob/__init__.py +0 -20
  175. mlrun/runtimes/mpijob/abstract.py +8 -8
  176. mlrun/runtimes/mpijob/v1.py +1 -1
  177. mlrun/runtimes/nuclio/api_gateway.py +646 -177
  178. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  179. mlrun/runtimes/nuclio/application/application.py +758 -0
  180. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  181. mlrun/runtimes/nuclio/function.py +188 -68
  182. mlrun/runtimes/nuclio/serving.py +57 -60
  183. mlrun/runtimes/pod.py +191 -58
  184. mlrun/runtimes/remotesparkjob.py +11 -8
  185. mlrun/runtimes/sparkjob/spark3job.py +17 -18
  186. mlrun/runtimes/utils.py +40 -73
  187. mlrun/secrets.py +6 -2
  188. mlrun/serving/__init__.py +8 -1
  189. mlrun/serving/remote.py +2 -3
  190. mlrun/serving/routers.py +89 -64
  191. mlrun/serving/server.py +54 -26
  192. mlrun/serving/states.py +187 -56
  193. mlrun/serving/utils.py +19 -11
  194. mlrun/serving/v2_serving.py +136 -63
  195. mlrun/track/tracker.py +2 -1
  196. mlrun/track/trackers/mlflow_tracker.py +5 -0
  197. mlrun/utils/async_http.py +26 -6
  198. mlrun/utils/db.py +18 -0
  199. mlrun/utils/helpers.py +375 -105
  200. mlrun/utils/http.py +2 -2
  201. mlrun/utils/logger.py +75 -9
  202. mlrun/utils/notifications/notification/__init__.py +14 -10
  203. mlrun/utils/notifications/notification/base.py +48 -0
  204. mlrun/utils/notifications/notification/console.py +2 -0
  205. mlrun/utils/notifications/notification/git.py +24 -1
  206. mlrun/utils/notifications/notification/ipython.py +2 -0
  207. mlrun/utils/notifications/notification/slack.py +96 -21
  208. mlrun/utils/notifications/notification/webhook.py +63 -2
  209. mlrun/utils/notifications/notification_pusher.py +146 -16
  210. mlrun/utils/regex.py +9 -0
  211. mlrun/utils/retryer.py +3 -2
  212. mlrun/utils/v3io_clients.py +2 -3
  213. mlrun/utils/version/version.json +2 -2
  214. mlrun-1.7.2.dist-info/METADATA +390 -0
  215. mlrun-1.7.2.dist-info/RECORD +351 -0
  216. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/WHEEL +1 -1
  217. mlrun/feature_store/retrieval/conversion.py +0 -271
  218. mlrun/kfpops.py +0 -868
  219. mlrun/model_monitoring/application.py +0 -310
  220. mlrun/model_monitoring/batch.py +0 -974
  221. mlrun/model_monitoring/controller_handler.py +0 -37
  222. mlrun/model_monitoring/prometheus.py +0 -216
  223. mlrun/model_monitoring/stores/__init__.py +0 -111
  224. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +0 -574
  225. mlrun/model_monitoring/stores/model_endpoint_store.py +0 -145
  226. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  227. mlrun/model_monitoring/stores/models/base.py +0 -84
  228. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  229. mlrun/platforms/other.py +0 -305
  230. mlrun-1.7.0rc5.dist-info/METADATA +0 -269
  231. mlrun-1.7.0rc5.dist-info/RECORD +0 -323
  232. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/LICENSE +0 -0
  233. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/entry_points.txt +0 -0
  234. {mlrun-1.7.0rc5.dist-info → mlrun-1.7.2.dist-info}/top_level.txt +0 -0
mlrun/execution.py CHANGED
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import logging
15
16
  import os
16
17
  import uuid
17
18
  from copy import deepcopy
@@ -22,6 +23,8 @@ import yaml
22
23
  from dateutil import parser
23
24
 
24
25
  import mlrun
26
+ import mlrun.common.constants as mlrun_constants
27
+ import mlrun.common.formatters
25
28
  from mlrun.artifacts import ModelArtifact
26
29
  from mlrun.datastore.store_resources import get_store_resource
27
30
  from mlrun.errors import MLRunInvalidArgumentError
@@ -33,13 +36,13 @@ from .features import Feature
33
36
  from .model import HyperParamOptions
34
37
  from .secrets import SecretsStore
35
38
  from .utils import (
39
+ RunKeys,
36
40
  dict_to_json,
37
41
  dict_to_yaml,
38
42
  get_in,
39
43
  is_relative_path,
40
44
  logger,
41
45
  now_date,
42
- run_keys,
43
46
  to_date_str,
44
47
  update_in,
45
48
  )
@@ -77,13 +80,13 @@ class MLClientCtx:
77
80
  self._tmpfile = tmp
78
81
  self._logger = log_stream or logger
79
82
  self._log_level = "info"
80
- self._matrics_db = None
81
83
  self._autocommit = autocommit
82
84
  self._notifications = []
83
85
  self._state_thresholds = {}
84
86
 
85
87
  self._labels = {}
86
88
  self._annotations = {}
89
+ self._node_selector = {}
87
90
 
88
91
  self._function = ""
89
92
  self._parameters = {}
@@ -101,8 +104,7 @@ class MLClientCtx:
101
104
  self._error = None
102
105
  self._commit = ""
103
106
  self._host = None
104
- self._start_time = now_date()
105
- self._last_update = now_date()
107
+ self._start_time = self._last_update = now_date()
106
108
  self._iteration_results = None
107
109
  self._children = []
108
110
  self._parent = None
@@ -110,6 +112,7 @@ class MLClientCtx:
110
112
 
111
113
  self._project_object = None
112
114
  self._allow_empty_resources = None
115
+ self._reset_on_run = None
113
116
 
114
117
  def __enter__(self):
115
118
  return self
@@ -129,7 +132,9 @@ class MLClientCtx:
129
132
  @property
130
133
  def tag(self):
131
134
  """Run tag (uid or workflow id if exists)"""
132
- return self._labels.get("workflow") or self._uid
135
+ return (
136
+ self._labels.get(mlrun_constants.MLRunInternalLabels.workflow) or self._uid
137
+ )
133
138
 
134
139
  @property
135
140
  def state(self):
@@ -165,6 +170,8 @@ class MLClientCtx:
165
170
  @log_level.setter
166
171
  def log_level(self, value: str):
167
172
  """Set the logging level, e.g. 'debug', 'info', 'error'"""
173
+ level = logging.getLevelName(value.upper())
174
+ self._logger.set_logger_level(level)
168
175
  self._log_level = value
169
176
 
170
177
  @property
@@ -203,6 +210,11 @@ class MLClientCtx:
203
210
  """Dictionary with labels (read-only)"""
204
211
  return deepcopy(self._labels)
205
212
 
213
+ @property
214
+ def node_selector(self):
215
+ """Dictionary with node selectors (read-only)"""
216
+ return deepcopy(self._node_selector)
217
+
206
218
  @property
207
219
  def annotations(self):
208
220
  """Dictionary with annotations (read-only)"""
@@ -224,12 +236,12 @@ class MLClientCtx:
224
236
  with context.get_child_context(myparam=param) as child:
225
237
  accuracy = child_handler(child, df, **child.parameters)
226
238
  accuracy_sum += accuracy
227
- child.log_result('accuracy', accuracy)
239
+ child.log_result("accuracy", accuracy)
228
240
  if accuracy > best_accuracy:
229
241
  child.mark_as_best()
230
242
  best_accuracy = accuracy
231
243
 
232
- context.log_result('avg_accuracy', accuracy_sum / len(param_list))
244
+ context.log_result("avg_accuracy", accuracy_sum / len(param_list))
233
245
 
234
246
  :param params: Extra (or override) params to parent context
235
247
  :param with_parent_params: Child will copy the parent parameters and add to them
@@ -289,7 +301,9 @@ class MLClientCtx:
289
301
 
290
302
  Example::
291
303
 
292
- feature_vector = context.get_store_resource("store://feature-vectors/default/myvec")
304
+ feature_vector = context.get_store_resource(
305
+ "store://feature-vectors/default/myvec"
306
+ )
293
307
  dataset = context.get_store_resource("store://artifacts/default/mydata")
294
308
 
295
309
  :param url: Store resource uri/path, store://<type>/<project>/<name>:<version>
@@ -325,10 +339,12 @@ class MLClientCtx:
325
339
  "name": self.name,
326
340
  "kind": "run",
327
341
  "uri": uri,
328
- "owner": get_in(self._labels, "owner"),
342
+ "owner": get_in(self._labels, mlrun_constants.MLRunInternalLabels.owner),
329
343
  }
330
- if "workflow" in self._labels:
331
- resp["workflow"] = self._labels["workflow"]
344
+ if mlrun_constants.MLRunInternalLabels.workflow in self._labels:
345
+ resp[mlrun_constants.MLRunInternalLabels.workflow] = self._labels[
346
+ mlrun_constants.MLRunInternalLabels.workflow
347
+ ]
332
348
  return resp
333
349
 
334
350
  @classmethod
@@ -357,7 +373,7 @@ class MLClientCtx:
357
373
  self._labels = meta.get("labels", self._labels)
358
374
  spec = attrs.get("spec")
359
375
  if spec:
360
- self._secrets_manager = SecretsStore.from_list(spec.get(run_keys.secrets))
376
+ self._secrets_manager = SecretsStore.from_list(spec.get(RunKeys.secrets))
361
377
  self._log_level = spec.get("log_level", self._log_level)
362
378
  self._function = spec.get("function", self._function)
363
379
  self._parameters = spec.get("parameters", self._parameters)
@@ -375,13 +391,15 @@ class MLClientCtx:
375
391
  self._allow_empty_resources = spec.get(
376
392
  "allow_empty_resources", self._allow_empty_resources
377
393
  )
378
- self.artifact_path = spec.get(run_keys.output_path, self.artifact_path)
379
- self._in_path = spec.get(run_keys.input_path, self._in_path)
380
- inputs = spec.get(run_keys.inputs)
394
+ self.artifact_path = spec.get(RunKeys.output_path, self.artifact_path)
395
+ self._in_path = spec.get(RunKeys.input_path, self._in_path)
396
+ inputs = spec.get(RunKeys.inputs)
381
397
  self._notifications = spec.get("notifications", self._notifications)
382
398
  self._state_thresholds = spec.get(
383
399
  "state_thresholds", self._state_thresholds
384
400
  )
401
+ self._node_selector = spec.get("node_selector", self._node_selector)
402
+ self._reset_on_run = spec.get("reset_on_run", self._reset_on_run)
385
403
 
386
404
  self._init_dbs(rundb)
387
405
 
@@ -394,7 +412,7 @@ class MLClientCtx:
394
412
  self._set_input(k, v)
395
413
 
396
414
  if host and not is_api:
397
- self.set_label("host", host)
415
+ self.set_label(mlrun_constants.MLRunInternalLabels.host, host)
398
416
 
399
417
  start = get_in(attrs, "status.start_time")
400
418
  if start:
@@ -421,7 +439,7 @@ class MLClientCtx:
421
439
 
422
440
  Example::
423
441
 
424
- data_path=context.artifact_subpath('data')
442
+ data_path = context.artifact_subpath("data")
425
443
 
426
444
  """
427
445
  return os.path.join(self.artifact_path, *subpaths)
@@ -525,7 +543,7 @@ class MLClientCtx:
525
543
 
526
544
  Example::
527
545
 
528
- context.log_result('accuracy', 0.85)
546
+ context.log_result("accuracy", 0.85)
529
547
 
530
548
  :param key: Result key
531
549
  :param value: Result value
@@ -539,7 +557,7 @@ class MLClientCtx:
539
557
 
540
558
  Example::
541
559
 
542
- context.log_results({'accuracy': 0.85, 'loss': 0.2})
560
+ context.log_results({"accuracy": 0.85, "loss": 0.2})
543
561
 
544
562
  :param results: Key/value dict or results
545
563
  :param commit: Commit (write to DB now vs wait for the end of the run)
@@ -558,7 +576,7 @@ class MLClientCtx:
558
576
  self._results["best_iteration"] = best
559
577
  for k, v in get_in(task, ["status", "results"], {}).items():
560
578
  self._results[k] = v
561
- for artifact in get_in(task, ["status", run_keys.artifacts], []):
579
+ for artifact in get_in(task, ["status", RunKeys.artifacts], []):
562
580
  self._artifacts_manager.artifacts[artifact["metadata"]["key"]] = (
563
581
  artifact
564
582
  )
@@ -617,7 +635,9 @@ class MLClientCtx:
617
635
  :param viewer: Kubeflow viewer type
618
636
  :param target_path: Absolute target path (instead of using artifact_path + local_path)
619
637
  :param src_path: Deprecated, use local_path
620
- :param upload: Upload to datastore (default is True)
638
+ :param upload: Whether to upload the artifact to the datastore. If not provided, and the `local_path`
639
+ is not a directory, upload occurs by default. Directories are uploaded only when this
640
+ flag is explicitly set to `True`.
621
641
  :param labels: A set of key/value labels to tag the artifact with
622
642
  :param format: Optional, format to use (e.g. csv, parquet, ..)
623
643
  :param db_key: The key to use in the artifact DB table, by default its run name + '_' + key
@@ -674,7 +694,9 @@ class MLClientCtx:
674
694
  "age": [42, 52, 36, 24, 73],
675
695
  "testScore": [25, 94, 57, 62, 70],
676
696
  }
677
- df = pd.DataFrame(raw_data, columns=["first_name", "last_name", "age", "testScore"])
697
+ df = pd.DataFrame(
698
+ raw_data, columns=["first_name", "last_name", "age", "testScore"]
699
+ )
678
700
  context.log_dataset("mydf", df=df, stats=True)
679
701
 
680
702
  :param key: Artifact key
@@ -752,13 +774,16 @@ class MLClientCtx:
752
774
 
753
775
  Example::
754
776
 
755
- context.log_model("model", body=dumps(model),
756
- model_file="model.pkl",
757
- metrics=context.results,
758
- training_set=training_df,
759
- label_column='label',
760
- feature_vector=feature_vector_uri,
761
- labels={"app": "fraud"})
777
+ context.log_model(
778
+ "model",
779
+ body=dumps(model),
780
+ model_file="model.pkl",
781
+ metrics=context.results,
782
+ training_set=training_df,
783
+ label_column="label",
784
+ feature_vector=feature_vector_uri,
785
+ labels={"app": "fraud"},
786
+ )
762
787
 
763
788
  :param key: Artifact key or artifact class ()
764
789
  :param body: Will use the body as the artifact content
@@ -902,6 +927,43 @@ class MLClientCtx:
902
927
  updates, self._uid, self.project, iter=self._iteration
903
928
  )
904
929
 
930
+ def get_notifications(self, unmask_secret_params=False):
931
+ """
932
+ Get the list of notifications
933
+
934
+ :param unmask_secret_params: Used as a workaround for sending notification from workflow-runner.
935
+ When used, if the notification will be saved again a new secret will be created.
936
+ """
937
+
938
+ # Get the full notifications from the DB since the run context does not contain the params due to bloating
939
+ run = self._rundb.read_run(
940
+ self.uid, format_=mlrun.common.formatters.RunFormat.notifications
941
+ )
942
+
943
+ notifications = []
944
+ for notification in run["spec"]["notifications"]:
945
+ notification: mlrun.model.Notification = mlrun.model.Notification.from_dict(
946
+ notification
947
+ )
948
+ # Fill the secret params from the project secret. We cannot use the server side internal secret mechanism
949
+ # here as it is the client side.
950
+ # TODO: This is a workaround to allow the notification to get the secret params from project secret
951
+ # instead of getting them from the internal project secret that should be mounted.
952
+ # We should mount the internal project secret that was created to the workflow-runner
953
+ # and get the secret from there.
954
+ if unmask_secret_params:
955
+ try:
956
+ notification.enrich_unmasked_secret_params_from_project_secret()
957
+ notifications.append(notification)
958
+ except mlrun.errors.MLRunValueError:
959
+ logger.warning(
960
+ "Failed to fill secret params from project secret for notification."
961
+ "Skip this notification.",
962
+ notification=notification.name,
963
+ )
964
+
965
+ return notifications
966
+
905
967
  def to_dict(self):
906
968
  """Convert the run context to a dictionary"""
907
969
 
@@ -925,10 +987,11 @@ class MLClientCtx:
925
987
  "parameters": self._parameters,
926
988
  "handler": self._handler,
927
989
  "outputs": self._outputs,
928
- run_keys.output_path: self.artifact_path,
929
- run_keys.inputs: self._inputs,
990
+ RunKeys.output_path: self.artifact_path,
991
+ RunKeys.inputs: self._inputs,
930
992
  "notifications": self._notifications,
931
993
  "state_thresholds": self._state_thresholds,
994
+ "node_selector": self._node_selector,
932
995
  },
933
996
  "status": {
934
997
  "results": self._results,
@@ -950,7 +1013,7 @@ class MLClientCtx:
950
1013
  set_if_not_none(struct["status"], "commit", self._commit)
951
1014
  set_if_not_none(struct["status"], "iterations", self._iteration_results)
952
1015
 
953
- struct["status"][run_keys.artifacts] = self._artifacts_manager.artifact_list()
1016
+ struct["status"][RunKeys.artifacts] = self._artifacts_manager.artifact_list()
954
1017
  self._data_stores.to_dict(struct["spec"])
955
1018
  return struct
956
1019
 
@@ -983,10 +1046,15 @@ class MLClientCtx:
983
1046
  # If it's a OpenMPI job, get the global rank and compare to the logging rank (worker) set in MLRun's
984
1047
  # configuration:
985
1048
  labels = self.labels
986
- if "host" in labels and labels.get("kind", "job") == "mpijob":
1049
+ if (
1050
+ mlrun_constants.MLRunInternalLabels.host in labels
1051
+ and labels.get(mlrun_constants.MLRunInternalLabels.kind, "job") == "mpijob"
1052
+ ):
987
1053
  # The host (pod name) of each worker is created by k8s, and by default it uses the rank number as the id in
988
1054
  # the following template: ...-worker-<rank>
989
- rank = int(labels["host"].rsplit("-", 1)[1])
1055
+ rank = int(
1056
+ labels[mlrun_constants.MLRunInternalLabels.host].rsplit("-", 1)[1]
1057
+ )
990
1058
  return rank == mlrun.mlconf.packagers.logging_worker
991
1059
 
992
1060
  # Single worker is always the logging worker:
@@ -1022,9 +1090,14 @@ class MLClientCtx:
1022
1090
  "status.last_update": to_date_str(self._last_update),
1023
1091
  }
1024
1092
 
1025
- # completion of runs is not decided by the execution as there may be
1026
- # multiple executions for a single run (e.g. mpi)
1027
- if self._state != "completed":
1093
+ # Completion of runs is decided by the API runs monitoring as there may be
1094
+ # multiple executions for a single run (e.g. mpi).
1095
+ # For kinds that are not monitored by the API (local) we allow changing the state.
1096
+ run_kind = self.labels.get(mlrun_constants.MLRunInternalLabels.kind, "")
1097
+ if (
1098
+ mlrun.runtimes.RuntimeKinds.is_local_runtime(run_kind)
1099
+ or self._state != "completed"
1100
+ ):
1028
1101
  struct["status.state"] = self._state
1029
1102
 
1030
1103
  if self.is_logging_worker():
@@ -1034,7 +1107,7 @@ class MLClientCtx:
1034
1107
  set_if_not_none(struct, "status.commit", self._commit)
1035
1108
  set_if_not_none(struct, "status.iterations", self._iteration_results)
1036
1109
 
1037
- struct[f"status.{run_keys.artifacts}"] = self._artifacts_manager.artifact_list()
1110
+ struct[f"status.{RunKeys.artifacts}"] = self._artifacts_manager.artifact_list()
1038
1111
  return struct
1039
1112
 
1040
1113
  def _init_dbs(self, rundb):
@@ -19,7 +19,6 @@ __all__ = [
19
19
  "get_online_feature_service",
20
20
  "ingest",
21
21
  "preview",
22
- "deploy_ingestion_service",
23
22
  "deploy_ingestion_service_v2",
24
23
  "delete_feature_set",
25
24
  "delete_feature_vector",
@@ -41,7 +40,6 @@ from ..features import Entity, Feature
41
40
  from .api import (
42
41
  delete_feature_set,
43
42
  delete_feature_vector,
44
- deploy_ingestion_service,
45
43
  deploy_ingestion_service_v2,
46
44
  get_feature_set,
47
45
  get_feature_vector,
@@ -113,6 +113,7 @@ def get_offline_features(
113
113
  order_by: Union[str, list[str]] = None,
114
114
  spark_service: str = None,
115
115
  timestamp_for_filtering: Union[str, dict[str, str]] = None,
116
+ additional_filters: list = None,
116
117
  ):
117
118
  """retrieve offline feature vector results
118
119
 
@@ -136,7 +137,10 @@ def get_offline_features(
136
137
  ]
137
138
  vector = FeatureVector(features=features)
138
139
  resp = get_offline_features(
139
- vector, entity_rows=trades, entity_timestamp_column="time", query="ticker in ['GOOG'] and bid>100"
140
+ vector,
141
+ entity_rows=trades,
142
+ entity_timestamp_column="time",
143
+ query="ticker in ['GOOG'] and bid>100",
140
144
  )
141
145
  print(resp.to_dataframe())
142
146
  print(vector.get_stats_table())
@@ -172,6 +176,13 @@ def get_offline_features(
172
176
  By default, the filter executes on the timestamp_key of each feature set.
173
177
  Note: the time filtering is performed on each feature set before the
174
178
  merge process using start_time and end_time params.
179
+ :param additional_filters: List of additional_filter conditions as tuples.
180
+ Each tuple should be in the format (column_name, operator, value).
181
+ Supported operators: "=", ">=", "<=", ">", "<".
182
+ Example: [("Product", "=", "Computer")]
183
+ For all supported filters, please see:
184
+ https://arrow.apache.org/docs/python/generated/pyarrow.parquet.ParquetDataset.html
185
+
175
186
 
176
187
  """
177
188
  return _get_offline_features(
@@ -191,6 +202,7 @@ def get_offline_features(
191
202
  order_by,
192
203
  spark_service,
193
204
  timestamp_for_filtering,
205
+ additional_filters,
194
206
  )
195
207
 
196
208
 
@@ -211,12 +223,18 @@ def _get_offline_features(
211
223
  order_by: Union[str, list[str]] = None,
212
224
  spark_service: str = None,
213
225
  timestamp_for_filtering: Union[str, dict[str, str]] = None,
226
+ additional_filters=None,
214
227
  ) -> Union[OfflineVectorResponse, RemoteVectorResponse]:
215
228
  if entity_rows is None and entity_timestamp_column is not None:
216
229
  raise mlrun.errors.MLRunInvalidArgumentError(
217
230
  "entity_timestamp_column param "
218
231
  "can not be specified without entity_rows param"
219
232
  )
233
+ if isinstance(target, BaseStoreTarget) and not target.support_pandas:
234
+ raise mlrun.errors.MLRunInvalidArgumentError(
235
+ f"get_offline_features does not support targets that do not support pandas engine."
236
+ f" Target kind: {target.kind}"
237
+ )
220
238
 
221
239
  if isinstance(feature_vector, FeatureVector):
222
240
  update_stats = True
@@ -249,6 +267,7 @@ def _get_offline_features(
249
267
  start_time=start_time,
250
268
  end_time=end_time,
251
269
  timestamp_for_filtering=timestamp_for_filtering,
270
+ additional_filters=additional_filters,
252
271
  )
253
272
 
254
273
  merger = merger_engine(feature_vector, **(engine_args or {}))
@@ -264,6 +283,7 @@ def _get_offline_features(
264
283
  update_stats=update_stats,
265
284
  query=query,
266
285
  order_by=order_by,
286
+ additional_filters=additional_filters,
267
287
  )
268
288
 
269
289
 
@@ -307,7 +327,7 @@ def get_online_feature_service(
307
327
 
308
328
  Example::
309
329
 
310
- svc = get_online_feature_service(vector_uri, entity_keys=['ticker'])
330
+ svc = get_online_feature_service(vector_uri, entity_keys=["ticker"])
311
331
  try:
312
332
  resp = svc.get([{"ticker": "GOOG"}, {"ticker": "MSFT"}])
313
333
  print(resp)
@@ -456,7 +476,7 @@ def ingest(
456
476
  df = ingest(stocks_set, stocks, infer_options=fstore.InferOptions.default())
457
477
 
458
478
  # for running as remote job
459
- config = RunConfig(image='mlrun/mlrun')
479
+ config = RunConfig(image="mlrun/mlrun")
460
480
  df = ingest(stocks_set, stocks, run_config=config)
461
481
 
462
482
  # specify source and targets
@@ -1002,53 +1022,6 @@ def _deploy_ingestion_service_v2(
1002
1022
  return function.deploy(), function
1003
1023
 
1004
1024
 
1005
- @deprecated(
1006
- version="1.5.0",
1007
- reason="'deploy_ingestion_service' will be removed in 1.7.0, use 'deploy_ingestion_service_v2' instead",
1008
- category=FutureWarning,
1009
- )
1010
- def deploy_ingestion_service(
1011
- featureset: Union[FeatureSet, str],
1012
- source: DataSource = None,
1013
- targets: list[DataTargetBase] = None,
1014
- name: str = None,
1015
- run_config: RunConfig = None,
1016
- verbose=False,
1017
- ) -> str:
1018
- """Start real-time ingestion service using nuclio function
1019
-
1020
- Deploy a real-time function implementing feature ingestion pipeline
1021
- the source maps to Nuclio event triggers (http, kafka, v3io stream, etc.)
1022
-
1023
- the `run_config` parameter allow specifying the function and job configuration,
1024
- see: :py:class:`~mlrun.feature_store.RunConfig`
1025
-
1026
- example::
1027
-
1028
- source = HTTPSource()
1029
- func = mlrun.code_to_function("ingest", kind="serving").apply(mount_v3io())
1030
- config = RunConfig(function=func)
1031
- my_set.deploy_ingestion_service(source, run_config=config)
1032
-
1033
- :param featureset: feature set object or uri
1034
- :param source: data source object describing the online or offline source
1035
- :param targets: list of data target objects
1036
- :param name: name for the job/function
1037
- :param run_config: service runtime configuration (function object/uri, resources, etc..)
1038
- :param verbose: verbose log
1039
-
1040
- :return: URL to access the deployed ingestion service
1041
- """
1042
- endpoint, _ = featureset.deploy_ingestion_service(
1043
- source=source,
1044
- targets=targets,
1045
- name=name,
1046
- run_config=run_config,
1047
- verbose=verbose,
1048
- )
1049
- return endpoint
1050
-
1051
-
1052
1025
  def _ingest_with_spark(
1053
1026
  spark=None,
1054
1027
  featureset: Union[FeatureSet, str] = None,
@@ -1064,6 +1037,8 @@ def _ingest_with_spark(
1064
1037
  try:
1065
1038
  import pyspark.sql
1066
1039
 
1040
+ from mlrun.datastore.spark_utils import check_special_columns_exists
1041
+
1067
1042
  if spark is None or spark is True:
1068
1043
  # create spark context
1069
1044
 
@@ -1076,13 +1051,13 @@ def _ingest_with_spark(
1076
1051
 
1077
1052
  spark = (
1078
1053
  pyspark.sql.SparkSession.builder.appName(session_name)
1054
+ .config("spark.driver.memory", "2g")
1079
1055
  .config("spark.sql.session.timeZone", "UTC")
1080
1056
  .getOrCreate()
1081
1057
  )
1082
1058
  created_spark_context = True
1083
1059
 
1084
1060
  timestamp_key = featureset.spec.timestamp_key
1085
-
1086
1061
  if isinstance(source, pd.DataFrame):
1087
1062
  df = spark.createDataFrame(source)
1088
1063
  elif isinstance(source, pyspark.sql.DataFrame):
@@ -1112,6 +1087,12 @@ def _ingest_with_spark(
1112
1087
  target = get_target_driver(target, featureset)
1113
1088
  target.set_resource(featureset)
1114
1089
  if featureset.spec.passthrough and target.is_offline:
1090
+ check_special_columns_exists(
1091
+ spark_df=df,
1092
+ entities=featureset.spec.entities,
1093
+ timestamp_key=timestamp_key,
1094
+ label_column=featureset.spec.label_column,
1095
+ )
1115
1096
  continue
1116
1097
  spark_options = target.get_spark_options(
1117
1098
  key_columns, timestamp_key, overwrite
@@ -1121,9 +1102,21 @@ def _ingest_with_spark(
1121
1102
  df_to_write = target.prepare_spark_df(
1122
1103
  df_to_write, key_columns, timestamp_key, spark_options
1123
1104
  )
1105
+ write_format = spark_options.pop("format", None)
1106
+ # We can get to this point if the column exists in different letter cases,
1107
+ # so PySpark will be able to read it, but we still have to raise an exception for it.
1108
+
1109
+ # This check is here and not in to_spark_df because in spark_merger we can have a target
1110
+ # that has different letter cases than the source, like in SnowflakeTarget.
1111
+ check_special_columns_exists(
1112
+ spark_df=df_to_write,
1113
+ entities=featureset.spec.entities,
1114
+ timestamp_key=timestamp_key,
1115
+ label_column=featureset.spec.label_column,
1116
+ )
1124
1117
  if overwrite:
1125
1118
  write_spark_dataframe_with_options(
1126
- spark_options, df_to_write, "overwrite"
1119
+ spark_options, df_to_write, "overwrite", write_format=write_format
1127
1120
  )
1128
1121
  else:
1129
1122
  # appending an empty dataframe may cause an empty file to be created (e.g. when writing to parquet)
@@ -1131,7 +1124,7 @@ def _ingest_with_spark(
1131
1124
  df_to_write.persist()
1132
1125
  if df_to_write.count() > 0:
1133
1126
  write_spark_dataframe_with_options(
1134
- spark_options, df_to_write, "append"
1127
+ spark_options, df_to_write, "append", write_format=write_format
1135
1128
  )
1136
1129
  target.update_resource_status("ready")
1137
1130
 
@@ -37,17 +37,12 @@ def parse_feature_string(feature):
37
37
  raise mlrun.errors.MLRunInvalidArgumentError(
38
38
  f"feature {feature} must be {expected_message}"
39
39
  )
40
- splitted = feature.split(feature_separator)
41
- if len(splitted) > 2:
42
- raise mlrun.errors.MLRunInvalidArgumentError(
43
- f"feature {feature} must be {expected_message}, cannot have more than one '.'"
44
- )
45
- feature_set = splitted[0]
46
- feature_name = splitted[1]
47
- splitted = feature_name.split(" as ")
48
- if len(splitted) > 1:
49
- return feature_set.strip(), splitted[0].strip(), splitted[1].strip()
50
- return feature_set.strip(), feature_name.strip(), None
40
+ feature_set, feature_name = feature.rsplit(feature_separator, 1)
41
+ feature_set = feature_set.strip()
42
+ split_result = feature_name.split(" as ", 1)
43
+ feature_name = split_result[0].strip()
44
+ alias = split_result[1].strip() if len(split_result) > 1 else None
45
+ return feature_set, feature_name, alias
51
46
 
52
47
 
53
48
  def parse_project_name_from_feature_string(feature):