mlrun 1.7.0rc9__py3-none-any.whl → 1.7.0rc11__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 (65) hide show
  1. mlrun/__init__.py +1 -0
  2. mlrun/artifacts/model.py +29 -25
  3. mlrun/common/schemas/__init__.py +1 -0
  4. mlrun/common/schemas/alert.py +122 -0
  5. mlrun/common/schemas/auth.py +4 -0
  6. mlrun/common/schemas/client_spec.py +1 -0
  7. mlrun/common/schemas/model_monitoring/constants.py +3 -1
  8. mlrun/config.py +8 -4
  9. mlrun/datastore/base.py +6 -5
  10. mlrun/datastore/sources.py +9 -4
  11. mlrun/datastore/targets.py +11 -3
  12. mlrun/datastore/v3io.py +27 -50
  13. mlrun/db/base.py +44 -2
  14. mlrun/db/httpdb.py +192 -20
  15. mlrun/db/nopdb.py +36 -1
  16. mlrun/execution.py +21 -14
  17. mlrun/feature_store/api.py +6 -3
  18. mlrun/feature_store/feature_set.py +39 -23
  19. mlrun/feature_store/feature_vector.py +2 -1
  20. mlrun/feature_store/steps.py +30 -19
  21. mlrun/features.py +4 -13
  22. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  23. mlrun/frameworks/lgbm/__init__.py +1 -1
  24. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  25. mlrun/frameworks/lgbm/model_handler.py +1 -1
  26. mlrun/frameworks/pytorch/__init__.py +2 -2
  27. mlrun/frameworks/sklearn/__init__.py +1 -1
  28. mlrun/frameworks/tf_keras/__init__.py +1 -1
  29. mlrun/frameworks/xgboost/__init__.py +1 -1
  30. mlrun/model.py +2 -2
  31. mlrun/model_monitoring/application.py +11 -2
  32. mlrun/model_monitoring/applications/histogram_data_drift.py +3 -3
  33. mlrun/model_monitoring/controller.py +2 -3
  34. mlrun/model_monitoring/stream_processing.py +0 -1
  35. mlrun/model_monitoring/writer.py +32 -0
  36. mlrun/package/packagers_manager.py +1 -0
  37. mlrun/platforms/__init__.py +1 -1
  38. mlrun/platforms/other.py +1 -1
  39. mlrun/projects/operations.py +11 -4
  40. mlrun/projects/project.py +148 -52
  41. mlrun/run.py +72 -40
  42. mlrun/runtimes/mpijob/abstract.py +8 -8
  43. mlrun/runtimes/nuclio/function.py +9 -5
  44. mlrun/runtimes/nuclio/serving.py +9 -8
  45. mlrun/runtimes/pod.py +3 -3
  46. mlrun/secrets.py +6 -2
  47. mlrun/serving/routers.py +3 -1
  48. mlrun/serving/states.py +12 -33
  49. mlrun/serving/v2_serving.py +4 -4
  50. mlrun/utils/helpers.py +1 -1
  51. mlrun/utils/notifications/notification/base.py +12 -0
  52. mlrun/utils/notifications/notification/console.py +2 -0
  53. mlrun/utils/notifications/notification/git.py +3 -1
  54. mlrun/utils/notifications/notification/ipython.py +2 -0
  55. mlrun/utils/notifications/notification/slack.py +41 -13
  56. mlrun/utils/notifications/notification/webhook.py +11 -1
  57. mlrun/utils/retryer.py +2 -2
  58. mlrun/utils/version/version.json +2 -2
  59. {mlrun-1.7.0rc9.dist-info → mlrun-1.7.0rc11.dist-info}/METADATA +1 -1
  60. {mlrun-1.7.0rc9.dist-info → mlrun-1.7.0rc11.dist-info}/RECORD +64 -64
  61. mlrun/datastore/helpers.py +0 -18
  62. {mlrun-1.7.0rc9.dist-info → mlrun-1.7.0rc11.dist-info}/LICENSE +0 -0
  63. {mlrun-1.7.0rc9.dist-info → mlrun-1.7.0rc11.dist-info}/WHEEL +0 -0
  64. {mlrun-1.7.0rc9.dist-info → mlrun-1.7.0rc11.dist-info}/entry_points.txt +0 -0
  65. {mlrun-1.7.0rc9.dist-info → mlrun-1.7.0rc11.dist-info}/top_level.txt +0 -0
mlrun/secrets.py CHANGED
@@ -163,15 +163,19 @@ def get_secret_or_env(
163
163
 
164
164
  Example::
165
165
 
166
- secrets = { "KEY1": "VALUE1" }
166
+ secrets = {"KEY1": "VALUE1"}
167
167
  secret = get_secret_or_env("KEY1", secret_provider=secrets)
168
168
 
169
+
169
170
  # Using a function to retrieve a secret
170
171
  def my_secret_provider(key):
171
172
  # some internal logic to retrieve secret
172
173
  return value
173
174
 
174
- secret = get_secret_or_env("KEY1", secret_provider=my_secret_provider, default="TOO-MANY-SECRETS")
175
+
176
+ secret = get_secret_or_env(
177
+ "KEY1", secret_provider=my_secret_provider, default="TOO-MANY-SECRETS"
178
+ )
175
179
 
176
180
  :param key: Secret key to look for
177
181
  :param secret_provider: Dictionary, callable or `SecretsStore` to extract the secret value from. If using a
mlrun/serving/routers.py CHANGED
@@ -272,7 +272,9 @@ class ParallelRun(BaseModelRouter):
272
272
  fn = mlrun.new_function("parallel", kind="serving")
273
273
  graph = fn.set_topology(
274
274
  "router",
275
- mlrun.serving.routers.ParallelRun(extend_event=True, executor_type=executor),
275
+ mlrun.serving.routers.ParallelRun(
276
+ extend_event=True, executor_type=executor
277
+ ),
276
278
  )
277
279
  graph.add_route("child1", class_name="Cls1")
278
280
  graph.add_route("child2", class_name="Cls2", my_arg={"c": 7})
mlrun/serving/states.py CHANGED
@@ -17,6 +17,7 @@ __all__ = ["TaskStep", "RouterStep", "RootFlowStep", "ErrorStep"]
17
17
  import os
18
18
  import pathlib
19
19
  import traceback
20
+ import warnings
20
21
  from copy import copy, deepcopy
21
22
  from inspect import getfullargspec, signature
22
23
  from typing import Union
@@ -590,7 +591,7 @@ class RouterStep(TaskStep):
590
591
 
591
592
  kind = "router"
592
593
  default_shape = "doubleoctagon"
593
- _dict_fields = _task_step_fields + ["routes", "engine"]
594
+ _dict_fields = _task_step_fields + ["routes"]
594
595
  _default_class = "mlrun.serving.ModelRouter"
595
596
 
596
597
  def __init__(
@@ -603,7 +604,6 @@ class RouterStep(TaskStep):
603
604
  function: str = None,
604
605
  input_path: str = None,
605
606
  result_path: str = None,
606
- engine: str = None,
607
607
  ):
608
608
  super().__init__(
609
609
  class_name,
@@ -616,8 +616,6 @@ class RouterStep(TaskStep):
616
616
  )
617
617
  self._routes: ObjectDict = None
618
618
  self.routes = routes
619
- self.engine = engine
620
- self._controller = None
621
619
 
622
620
  def get_children(self):
623
621
  """get child steps (routes)"""
@@ -687,33 +685,6 @@ class RouterStep(TaskStep):
687
685
  self._set_error_handler()
688
686
  self._post_init(mode)
689
687
 
690
- if self.engine == "async":
691
- self._build_async_flow()
692
- self._run_async_flow()
693
-
694
- def _build_async_flow(self):
695
- """initialize and build the async/storey DAG"""
696
-
697
- self.respond()
698
- source, self._wait_for_result = _init_async_objects(self.context, [self])
699
- source.to(self.async_object)
700
-
701
- self._async_flow = source
702
-
703
- def _run_async_flow(self):
704
- self._controller = self._async_flow.run()
705
-
706
- def run(self, event, *args, **kwargs):
707
- if self._controller:
708
- # async flow (using storey)
709
- event._awaitable_result = None
710
- resp = self._controller.emit(
711
- event, return_awaitable_result=self._wait_for_result
712
- )
713
- return resp.await_result()
714
-
715
- return super().run(event, *args, **kwargs)
716
-
717
688
  def __getitem__(self, name):
718
689
  return self._routes[name]
719
690
 
@@ -1524,13 +1495,21 @@ def _init_async_objects(context, steps):
1524
1495
  endpoint = None
1525
1496
  options = {}
1526
1497
  options.update(step.options)
1498
+
1527
1499
  kafka_brokers = options.pop("kafka_brokers", None)
1500
+ if not kafka_brokers and "kafka_bootstrap_servers" in options:
1501
+ kafka_brokers = options.pop("kafka_bootstrap_servers")
1502
+ warnings.warn(
1503
+ "The 'kafka_bootstrap_servers' parameter is deprecated and will be removed in "
1504
+ "1.9.0. Please pass the 'kafka_brokers' parameter instead.",
1505
+ FutureWarning,
1506
+ )
1507
+
1528
1508
  if stream_path.startswith("kafka://") or kafka_brokers:
1529
1509
  topic, brokers = parse_kafka_url(stream_path, kafka_brokers)
1530
1510
 
1531
1511
  kafka_producer_options = options.pop(
1532
- "kafka_producer_options",
1533
- options.pop("kafka_bootstrap_servers", None),
1512
+ "kafka_producer_options", None
1534
1513
  )
1535
1514
 
1536
1515
  step._async_object = storey.KafkaTarget(
@@ -63,11 +63,11 @@ class V2ModelServer(StepToDict):
63
63
  class MyClass(V2ModelServer):
64
64
  def load(self):
65
65
  # load and initialize the model and/or other elements
66
- model_file, extra_data = self.get_model(suffix='.pkl')
66
+ model_file, extra_data = self.get_model(suffix=".pkl")
67
67
  self.model = load(open(model_file, "rb"))
68
68
 
69
69
  def predict(self, request):
70
- events = np.array(request['inputs'])
70
+ events = np.array(request["inputs"])
71
71
  dmatrix = xgb.DMatrix(events)
72
72
  result: xgb.DMatrix = self.model.predict(dmatrix)
73
73
  return {"outputs": result.tolist()}
@@ -176,9 +176,9 @@ class V2ModelServer(StepToDict):
176
176
  ::
177
177
 
178
178
  def load(self):
179
- model_file, extra_data = self.get_model(suffix='.pkl')
179
+ model_file, extra_data = self.get_model(suffix=".pkl")
180
180
  self.model = load(open(model_file, "rb"))
181
- categories = extra_data['categories'].as_df()
181
+ categories = extra_data["categories"].as_df()
182
182
 
183
183
  Parameters
184
184
  ----------
mlrun/utils/helpers.py CHANGED
@@ -437,7 +437,7 @@ class LogBatchWriter:
437
437
 
438
438
  def get_in(obj, keys, default=None):
439
439
  """
440
- >>> get_in({'a': {'b': 1}}, 'a.b')
440
+ >>> get_in({"a": {"b": 1}}, "a.b")
441
441
  1
442
442
  """
443
443
  if isinstance(keys, str):
@@ -44,6 +44,8 @@ class NotificationBase:
44
44
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
45
45
  runs: typing.Union[mlrun.lists.RunList, list] = None,
46
46
  custom_html: str = None,
47
+ alert: mlrun.common.schemas.AlertConfig = None,
48
+ event_data: mlrun.common.schemas.Event = None,
47
49
  ):
48
50
  raise NotImplementedError()
49
51
 
@@ -61,6 +63,8 @@ class NotificationBase:
61
63
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
62
64
  runs: typing.Union[mlrun.lists.RunList, list] = None,
63
65
  custom_html: str = None,
66
+ alert: mlrun.common.schemas.AlertConfig = None,
67
+ event_data: mlrun.common.schemas.Event = None,
64
68
  ) -> str:
65
69
  if custom_html:
66
70
  return custom_html
@@ -68,6 +72,14 @@ class NotificationBase:
68
72
  if self.name:
69
73
  message = f"{self.name}: {message}"
70
74
 
75
+ if alert:
76
+ if not event_data:
77
+ return f"[{severity}] {message}"
78
+ return (
79
+ f"[{severity}] {message} for project {alert.project} "
80
+ f"UID {event_data.entity.id}. Value {event_data.value}"
81
+ )
82
+
71
83
  if not runs:
72
84
  return f"[{severity}] {message}"
73
85
 
@@ -36,6 +36,8 @@ class ConsoleNotification(NotificationBase):
36
36
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
37
37
  runs: typing.Union[mlrun.lists.RunList, list] = None,
38
38
  custom_html: str = None,
39
+ alert: mlrun.common.schemas.AlertConfig = None,
40
+ event_data: mlrun.common.schemas.Event = None,
39
41
  ):
40
42
  severity = self._resolve_severity(severity)
41
43
  print(f"[{severity}] {message}")
@@ -38,6 +38,8 @@ class GitNotification(NotificationBase):
38
38
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
39
39
  runs: typing.Union[mlrun.lists.RunList, list] = None,
40
40
  custom_html: str = None,
41
+ alert: mlrun.common.schemas.AlertConfig = None,
42
+ event_data: mlrun.common.schemas.Event = None,
41
43
  ):
42
44
  git_repo = self.params.get("repo", None)
43
45
  git_issue = self.params.get("issue", None)
@@ -50,7 +52,7 @@ class GitNotification(NotificationBase):
50
52
  server = self.params.get("server", None)
51
53
  gitlab = self.params.get("gitlab", False)
52
54
  await self._pr_comment(
53
- self._get_html(message, severity, runs, custom_html),
55
+ self._get_html(message, severity, runs, custom_html, alert, event_data),
54
56
  git_repo,
55
57
  git_issue,
56
58
  merge_request=git_merge_request,
@@ -53,6 +53,8 @@ class IPythonNotification(NotificationBase):
53
53
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
54
54
  runs: typing.Union[mlrun.lists.RunList, list] = None,
55
55
  custom_html: str = None,
56
+ alert: mlrun.common.schemas.AlertConfig = None,
57
+ event_data: mlrun.common.schemas.Event = None,
56
58
  ):
57
59
  if not self._ipython:
58
60
  mlrun.utils.helpers.logger.debug(
@@ -42,6 +42,8 @@ class SlackNotification(NotificationBase):
42
42
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
43
43
  runs: typing.Union[mlrun.lists.RunList, list] = None,
44
44
  custom_html: str = None,
45
+ alert: mlrun.common.schemas.AlertConfig = None,
46
+ event_data: mlrun.common.schemas.Event = None,
45
47
  ):
46
48
  webhook = self.params.get("webhook", None) or mlrun.get_secret_or_env(
47
49
  "SLACK_WEBHOOK"
@@ -53,7 +55,7 @@ class SlackNotification(NotificationBase):
53
55
  )
54
56
  return
55
57
 
56
- data = self._generate_slack_data(message, severity, runs)
58
+ data = self._generate_slack_data(message, severity, runs, alert, event_data)
57
59
 
58
60
  async with aiohttp.ClientSession() as session:
59
61
  async with session.post(webhook, json=data) as response:
@@ -66,12 +68,14 @@ class SlackNotification(NotificationBase):
66
68
  mlrun.common.schemas.NotificationSeverity, str
67
69
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
68
70
  runs: typing.Union[mlrun.lists.RunList, list] = None,
71
+ alert: mlrun.common.schemas.AlertConfig = None,
72
+ event_data: mlrun.common.schemas.Event = None,
69
73
  ) -> dict:
70
74
  data = {
71
75
  "blocks": [
72
76
  {
73
- "type": "section",
74
- "text": self._get_slack_row(f"[{severity}] {message}"),
77
+ "type": "header",
78
+ "text": {"type": "plain_text", "text": f"[{severity}] {message}"},
75
79
  },
76
80
  ]
77
81
  }
@@ -80,22 +84,46 @@ class SlackNotification(NotificationBase):
80
84
  {"type": "section", "text": self._get_slack_row(self.name)}
81
85
  )
82
86
 
83
- if not runs:
84
- return data
87
+ if alert:
88
+ fields = self._get_alert_fields(alert, event_data)
85
89
 
86
- if isinstance(runs, list):
87
- runs = mlrun.lists.RunList(runs)
90
+ for i in range(len(fields)):
91
+ data["blocks"].append({"type": "section", "text": fields[i]})
92
+ else:
93
+ if not runs:
94
+ return data
95
+
96
+ if isinstance(runs, list):
97
+ runs = mlrun.lists.RunList(runs)
88
98
 
89
- fields = [self._get_slack_row("*Runs*"), self._get_slack_row("*Results*")]
90
- for run in runs:
91
- fields.append(self._get_run_line(run))
92
- fields.append(self._get_run_result(run))
99
+ fields = [self._get_slack_row("*Runs*"), self._get_slack_row("*Results*")]
100
+ for run in runs:
101
+ fields.append(self._get_run_line(run))
102
+ fields.append(self._get_run_result(run))
93
103
 
94
- for i in range(0, len(fields), 8):
95
- data["blocks"].append({"type": "section", "fields": fields[i : i + 8]})
104
+ for i in range(0, len(fields), 8):
105
+ data["blocks"].append({"type": "section", "fields": fields[i : i + 8]})
96
106
 
97
107
  return data
98
108
 
109
+ def _get_alert_fields(
110
+ self,
111
+ alert: mlrun.common.schemas.AlertConfig,
112
+ event_data: mlrun.common.schemas.Event,
113
+ ) -> list:
114
+ line = [
115
+ self._get_slack_row(f":bell: {alert.name} alert has occurred"),
116
+ self._get_slack_row(f"*Project:*\n{alert.project}"),
117
+ self._get_slack_row(f"*UID:*\n{event_data.entity.id}"),
118
+ ]
119
+ if event_data.value is not None:
120
+ line.append(self._get_slack_row(f"*Event data:*\n{event_data.value}"))
121
+
122
+ if url := mlrun.utils.helpers.get_ui_url(alert.project, event_data.entity.id):
123
+ line.append(self._get_slack_row(f"*Overview:*\n<{url}|*Job overview*>"))
124
+
125
+ return line
126
+
99
127
  def _get_run_line(self, run: dict) -> dict:
100
128
  meta = run["metadata"]
101
129
  url = mlrun.utils.helpers.get_ui_url(meta.get("project"), meta.get("uid"))
@@ -36,6 +36,8 @@ class WebhookNotification(NotificationBase):
36
36
  ] = mlrun.common.schemas.NotificationSeverity.INFO,
37
37
  runs: typing.Union[mlrun.lists.RunList, list] = None,
38
38
  custom_html: str = None,
39
+ alert: mlrun.common.schemas.AlertConfig = None,
40
+ event_data: mlrun.common.schemas.Event = None,
39
41
  ):
40
42
  url = self.params.get("url", None)
41
43
  method = self.params.get("method", "post").lower()
@@ -46,9 +48,17 @@ class WebhookNotification(NotificationBase):
46
48
  request_body = {
47
49
  "message": message,
48
50
  "severity": severity,
49
- "runs": runs,
50
51
  }
51
52
 
53
+ if runs:
54
+ request_body["runs"] = runs
55
+
56
+ if alert:
57
+ request_body["alert"] = alert.dict()
58
+ if event_data:
59
+ request_body["value"] = event_data.value
60
+ request_body["id"] = event_data.entity.id
61
+
52
62
  if custom_html:
53
63
  request_body["custom_html"] = custom_html
54
64
 
mlrun/utils/retryer.py CHANGED
@@ -117,7 +117,7 @@ class Retryer:
117
117
  self._raise_last_exception()
118
118
 
119
119
  def _prepare(self):
120
- self.start_time = time.time()
120
+ self.start_time = time.monotonic()
121
121
  self.last_exception = None
122
122
 
123
123
  # Check if backoff is just a simple interval
@@ -173,7 +173,7 @@ class Retryer:
173
173
  ) from self.last_exception
174
174
 
175
175
  def _timeout_exceeded(self, next_interval=None):
176
- now = time.time()
176
+ now = time.monotonic()
177
177
  if next_interval:
178
178
  now = now + next_interval
179
179
  return self.timeout is not None and now >= self.start_time + self.timeout
@@ -1,4 +1,4 @@
1
1
  {
2
- "git_commit": "3396f9ef4c278411ca6227b1792ff7df20005a42",
3
- "version": "1.7.0-rc9"
2
+ "git_commit": "fbea0ad0a63c1a8af06793354df5d3c467ca606d",
3
+ "version": "1.7.0-rc11"
4
4
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mlrun
3
- Version: 1.7.0rc9
3
+ Version: 1.7.0rc11
4
4
  Summary: Tracking and config of machine learning runs
5
5
  Home-page: https://github.com/mlrun/mlrun
6
6
  Author: Yaron Haviv