mlrun 1.8.0rc5__py3-none-any.whl → 1.8.0rc7__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 (71) hide show
  1. mlrun/__init__.py +1 -0
  2. mlrun/artifacts/__init__.py +1 -1
  3. mlrun/artifacts/base.py +12 -1
  4. mlrun/artifacts/document.py +59 -38
  5. mlrun/common/constants.py +1 -0
  6. mlrun/common/model_monitoring/__init__.py +0 -2
  7. mlrun/common/model_monitoring/helpers.py +0 -28
  8. mlrun/common/schemas/__init__.py +2 -4
  9. mlrun/common/schemas/alert.py +77 -1
  10. mlrun/common/schemas/client_spec.py +0 -1
  11. mlrun/common/schemas/model_monitoring/__init__.py +0 -6
  12. mlrun/common/schemas/model_monitoring/constants.py +11 -9
  13. mlrun/common/schemas/model_monitoring/model_endpoints.py +77 -149
  14. mlrun/common/schemas/notification.py +6 -0
  15. mlrun/common/schemas/project.py +3 -0
  16. mlrun/config.py +2 -3
  17. mlrun/datastore/datastore_profile.py +57 -17
  18. mlrun/datastore/sources.py +1 -2
  19. mlrun/datastore/vectorstore.py +67 -59
  20. mlrun/db/base.py +29 -19
  21. mlrun/db/httpdb.py +218 -159
  22. mlrun/db/nopdb.py +36 -17
  23. mlrun/execution.py +14 -7
  24. mlrun/feature_store/api.py +1 -0
  25. mlrun/model.py +3 -0
  26. mlrun/model_monitoring/__init__.py +3 -2
  27. mlrun/model_monitoring/api.py +55 -53
  28. mlrun/model_monitoring/applications/_application_steps.py +3 -1
  29. mlrun/model_monitoring/applications/base.py +115 -15
  30. mlrun/model_monitoring/applications/context.py +42 -24
  31. mlrun/model_monitoring/applications/histogram_data_drift.py +1 -1
  32. mlrun/model_monitoring/controller.py +43 -37
  33. mlrun/model_monitoring/db/__init__.py +0 -2
  34. mlrun/model_monitoring/db/tsdb/base.py +2 -1
  35. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +2 -1
  36. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +43 -0
  37. mlrun/model_monitoring/helpers.py +79 -66
  38. mlrun/model_monitoring/stream_processing.py +83 -270
  39. mlrun/model_monitoring/writer.py +1 -10
  40. mlrun/projects/pipelines.py +37 -1
  41. mlrun/projects/project.py +147 -55
  42. mlrun/run.py +40 -0
  43. mlrun/runtimes/nuclio/function.py +7 -6
  44. mlrun/runtimes/nuclio/serving.py +9 -2
  45. mlrun/serving/routers.py +158 -145
  46. mlrun/serving/server.py +6 -0
  47. mlrun/serving/states.py +21 -7
  48. mlrun/serving/v2_serving.py +70 -61
  49. mlrun/utils/helpers.py +14 -30
  50. mlrun/utils/notifications/notification/mail.py +17 -6
  51. mlrun/utils/notifications/notification_pusher.py +9 -5
  52. mlrun/utils/version/version.json +2 -2
  53. {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc7.dist-info}/METADATA +2 -2
  54. {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc7.dist-info}/RECORD +58 -71
  55. mlrun/common/schemas/model_monitoring/model_endpoint_v2.py +0 -149
  56. mlrun/model_monitoring/db/stores/__init__.py +0 -136
  57. mlrun/model_monitoring/db/stores/base/__init__.py +0 -15
  58. mlrun/model_monitoring/db/stores/base/store.py +0 -154
  59. mlrun/model_monitoring/db/stores/sqldb/__init__.py +0 -13
  60. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -46
  61. mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -93
  62. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -47
  63. mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -25
  64. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -408
  65. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +0 -13
  66. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -464
  67. mlrun/model_monitoring/model_endpoint.py +0 -120
  68. {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc7.dist-info}/LICENSE +0 -0
  69. {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc7.dist-info}/WHEEL +0 -0
  70. {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc7.dist-info}/entry_points.txt +0 -0
  71. {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc7.dist-info}/top_level.txt +0 -0
@@ -21,10 +21,9 @@ import mlrun.artifacts
21
21
  import mlrun.common.model_monitoring.helpers
22
22
  import mlrun.common.schemas.model_monitoring
23
23
  import mlrun.model_monitoring
24
- from mlrun.errors import err_to_str
25
24
  from mlrun.utils import logger, now_date
26
25
 
27
- from ..common.helpers import parse_versioned_object_uri
26
+ from ..common.schemas.model_monitoring import ModelEndpointSchema
28
27
  from .server import GraphServer
29
28
  from .utils import StepToDict, _extract_input_data, _update_result_body
30
29
 
@@ -110,12 +109,6 @@ class V2ModelServer(StepToDict):
110
109
  self._result_path = result_path
111
110
  self._kwargs = kwargs # for to_dict()
112
111
  self._params = kwargs
113
- self._model_logger = (
114
- _ModelLogPusher(self, context)
115
- if context and context.stream.enabled
116
- else None
117
- )
118
-
119
112
  self.metrics = {}
120
113
  self.labels = {}
121
114
  self.model = None
@@ -125,6 +118,7 @@ class V2ModelServer(StepToDict):
125
118
  self._versioned_model_name = None
126
119
  self.model_endpoint_uid = None
127
120
  self.shard_by_endpoint = shard_by_endpoint
121
+ self._model_logger = None
128
122
 
129
123
  def _load_and_update_state(self):
130
124
  try:
@@ -157,6 +151,11 @@ class V2ModelServer(StepToDict):
157
151
  self.model_endpoint_uid = _init_endpoint_record(
158
152
  graph_server=server, model=self
159
153
  )
154
+ self._model_logger = (
155
+ _ModelLogPusher(self, self.context)
156
+ if self.context and self.context.stream.enabled
157
+ else None
158
+ )
160
159
 
161
160
  def get_param(self, key: str, default=None):
162
161
  """get param by key (specified in the model or the function)"""
@@ -474,7 +473,7 @@ class V2ModelServer(StepToDict):
474
473
 
475
474
 
476
475
  class _ModelLogPusher:
477
- def __init__(self, model, context, output_stream=None):
476
+ def __init__(self, model: V2ModelServer, context, output_stream=None):
478
477
  self.model = model
479
478
  self.verbose = context.verbose
480
479
  self.hostname = context.stream.hostname
@@ -496,6 +495,7 @@ class _ModelLogPusher:
496
495
  "version": self.model.version,
497
496
  "host": self.hostname,
498
497
  "function_uri": self.function_uri,
498
+ "endpoint_id": self.model.model_endpoint_uid,
499
499
  }
500
500
  if getattr(self.model, "labels", None):
501
501
  base_data["labels"] = self.model.labels
@@ -567,26 +567,13 @@ def _init_endpoint_record(
567
567
  """
568
568
 
569
569
  logger.info("Initializing endpoint records")
570
-
571
- # Generate required values for the model endpoint record
572
- try:
573
- # Getting project name from the function uri
574
- project, uri, tag, hash_key = parse_versioned_object_uri(
575
- graph_server.function_uri
576
- )
577
- except Exception as e:
578
- logger.error("Failed to parse function URI", exc=err_to_str(e))
579
- return None
580
-
581
- # Generating model endpoint ID based on function uri and model version
582
- uid = mlrun.common.model_monitoring.create_model_endpoint_uid(
583
- function_uri=graph_server.function_uri,
584
- versioned_model=model.versioned_model_name,
585
- ).uid
586
-
570
+ if not model.model_spec:
571
+ model.get_model()
587
572
  try:
588
573
  model_ep = mlrun.get_run_db().get_model_endpoint(
589
- project=project, endpoint_id=uid
574
+ project=graph_server.project,
575
+ name=model.name,
576
+ function_name=graph_server.function_name,
590
577
  )
591
578
  except mlrun.errors.MLRunNotFoundError:
592
579
  model_ep = None
@@ -596,62 +583,84 @@ def _init_endpoint_record(
596
583
  )
597
584
  return
598
585
 
599
- if model.context.server.track_models and not model_ep:
600
- logger.info("Creating a new model endpoint record", endpoint_id=uid)
601
- model_endpoint = mlrun.common.schemas.ModelEndpoint(
586
+ function = mlrun.get_run_db().get_function(
587
+ name=graph_server.function_name,
588
+ project=graph_server.project,
589
+ tag=graph_server.function_tag or "latest",
590
+ )
591
+ function_uid = function.get("metadata", {}).get("uid")
592
+ if not model_ep and model.context.server.track_models:
593
+ logger.info(
594
+ "Creating a new model endpoint record",
595
+ name=model.name,
596
+ project=graph_server.project,
597
+ function_name=graph_server.function_name,
598
+ function_uid=function_uid,
599
+ model_name=model.model_spec.metadata.key,
600
+ model_uid=model.model_spec.metadata.uid,
601
+ model_class=model.__class__.__name__,
602
+ model_tag=model.model_spec.tag,
603
+ )
604
+ model_ep = mlrun.common.schemas.ModelEndpoint(
602
605
  metadata=mlrun.common.schemas.ModelEndpointMetadata(
603
- project=project, labels=model.labels, uid=uid
606
+ project=graph_server.project,
607
+ labels=model.model_spec.labels,
608
+ name=model.name,
609
+ endpoint_type=mlrun.common.schemas.model_monitoring.EndpointType.NODE_EP,
604
610
  ),
605
611
  spec=mlrun.common.schemas.ModelEndpointSpec(
606
- function_uri=graph_server.function_uri,
607
- model=model.versioned_model_name,
612
+ function_name=graph_server.function_name,
613
+ function_uid=function_uid,
614
+ function_tag=graph_server.function_tag or "latest",
615
+ model_name=model.model_spec.metadata.key,
616
+ model_uid=model.model_spec.metadata.uid,
608
617
  model_class=model.__class__.__name__,
609
- model_uri=model.model_path,
610
- stream_path=model.context.stream.stream_uri,
611
- active=True,
612
- monitoring_mode=mlrun.common.schemas.model_monitoring.ModelMonitoringMode.enabled,
613
618
  ),
614
619
  status=mlrun.common.schemas.ModelEndpointStatus(
615
- endpoint_type=mlrun.common.schemas.model_monitoring.EndpointType.NODE_EP
620
+ monitoring_mode=mlrun.common.schemas.model_monitoring.ModelMonitoringMode.enabled
621
+ if model.context.server.track_models
622
+ else mlrun.common.schemas.model_monitoring.ModelMonitoringMode.disabled,
616
623
  ),
617
624
  )
618
-
619
625
  db = mlrun.get_run_db()
620
- db.create_model_endpoint(
621
- project=project,
622
- endpoint_id=uid,
623
- model_endpoint=model_endpoint.dict(),
624
- )
626
+ db.create_model_endpoint(model_endpoint=model_ep)
625
627
 
626
628
  elif model_ep:
627
629
  attributes = {}
628
- old_model_uri = model_ep.spec.model_uri
629
- mlrun.model_monitoring.helpers.enrich_model_endpoint_with_model_uri(
630
- model_endpoint=model_ep,
631
- model_obj=model.model_spec,
632
- )
633
- if model_ep.spec.model_uri != old_model_uri:
634
- attributes["model_uri"] = model_ep.spec.model_uri
630
+ if function_uid != model_ep.spec.function_uid:
631
+ attributes[ModelEndpointSchema.FUNCTION_UID] = function_uid
632
+ if model.model_spec.metadata.key != model_ep.spec.model_name:
633
+ attributes[ModelEndpointSchema.MODEL_NAME] = model.model_spec.metadata.key
634
+ if model.model_spec.metadata.uid != model_ep.spec.model_uid:
635
+ attributes[ModelEndpointSchema.MODEL_UID] = model.model_spec.metadata.uid
636
+ if model.__class__.__name__ != model_ep.spec.model_class:
637
+ attributes[ModelEndpointSchema.MODEL_CLASS] = model.__class__.__name__
635
638
  if (
636
- model_ep.spec.monitoring_mode
639
+ model_ep.status.monitoring_mode
637
640
  == mlrun.common.schemas.model_monitoring.ModelMonitoringMode.enabled
638
641
  ) != model.context.server.track_models:
639
- attributes["monitoring_mode"] = (
642
+ attributes[ModelEndpointSchema.MONITORING_MODE] = (
640
643
  mlrun.common.schemas.model_monitoring.ModelMonitoringMode.enabled
641
644
  if model.context.server.track_models
642
645
  else mlrun.common.schemas.model_monitoring.ModelMonitoringMode.disabled
643
646
  )
644
647
  if attributes:
645
- db = mlrun.get_run_db()
646
- db.patch_model_endpoint(
647
- project=project,
648
- endpoint_id=uid,
649
- attributes=attributes,
650
- )
651
648
  logger.info(
652
649
  "Updating model endpoint attributes",
653
650
  attributes=attributes,
654
- endpoint_id=uid,
651
+ project=model_ep.metadata.project,
652
+ name=model_ep.metadata.name,
653
+ function_name=model_ep.spec.function_name,
654
+ )
655
+ db = mlrun.get_run_db()
656
+ model_ep = db.patch_model_endpoint(
657
+ project=model_ep.metadata.project,
658
+ name=model_ep.metadata.name,
659
+ function_name=model_ep.spec.function_name,
660
+ endpoint_id=model_ep.metadata.uid,
661
+ attributes=attributes,
655
662
  )
663
+ else:
664
+ return None
656
665
 
657
- return uid
666
+ return model_ep.metadata.uid
mlrun/utils/helpers.py CHANGED
@@ -956,36 +956,6 @@ def fill_function_hash(function_dict, tag=""):
956
956
  return fill_object_hash(function_dict, "hash", tag)
957
957
 
958
958
 
959
- def fill_model_endpoint_hash(
960
- model_endpoint: mlrun.common.schemas.ModelEndpointV2,
961
- created_time: typing.Union[str, datetime],
962
- ) -> str:
963
- """
964
- Fill the model endpoint uid field in the model endpoint object and returns it.
965
- The uid is generated by hashing the following model endpoint fields:
966
- - name
967
- - model_tag
968
- - function_name
969
- - project
970
- - created_time
971
- """
972
-
973
- name = model_endpoint.metadata.name
974
- model_tag = model_endpoint.spec.model_tag
975
- function_name = model_endpoint.spec.function_name
976
- project = model_endpoint.metadata.project
977
- created_time = (
978
- created_time
979
- if isinstance(created_time, str)
980
- else created_time.isoformat(sep=" ", timespec="microseconds")
981
- )
982
-
983
- unique_string = f"{name}_{model_tag}_{function_name}_{project}_{created_time}"
984
- uid = hashlib.sha1(unique_string.encode("utf-8")).hexdigest()
985
- model_endpoint.metadata.uid = uid
986
- return uid
987
-
988
-
989
959
  def retry_until_successful(
990
960
  backoff: int, timeout: int, logger, verbose: bool, _function, *args, **kwargs
991
961
  ):
@@ -1886,3 +1856,17 @@ def run_with_retry(
1886
1856
  if attempt == retry_count:
1887
1857
  raise
1888
1858
  raise last_exception
1859
+
1860
+
1861
+ def join_urls(base_url: Optional[str], path: Optional[str]) -> str:
1862
+ """
1863
+ Joins a base URL with a path, ensuring proper handling of slashes.
1864
+
1865
+ :param base_url: The base URL (e.g., "http://example.com").
1866
+ :param path: The path to append to the base URL (e.g., "/path/to/resource").
1867
+
1868
+ :return: A unified URL with exactly one slash between base_url and path.
1869
+ """
1870
+ if base_url is None:
1871
+ base_url = ""
1872
+ return f"{base_url.rstrip('/')}/{path.lstrip('/')}" if path else base_url
@@ -13,7 +13,8 @@
13
13
  # limitations under the License.
14
14
  import re
15
15
  import typing
16
- from email.message import EmailMessage
16
+ from email.mime.multipart import MIMEMultipart
17
+ from email.mime.text import MIMEText
17
18
 
18
19
  import aiosmtplib
19
20
 
@@ -69,9 +70,19 @@ class MailNotification(base.NotificationBase):
69
70
  alert: typing.Optional[mlrun.common.schemas.AlertConfig] = None,
70
71
  event_data: typing.Optional[mlrun.common.schemas.Event] = None,
71
72
  ):
72
- message = self.params.get("message", message)
73
- self.params.setdefault("subject", f"[{severity}] {message}")
74
- self.params.setdefault("body", message)
73
+ self.params["subject"] = f"[{severity}] {message}"
74
+ message_body_override = self.params.get("message_body_override", None)
75
+
76
+ runs_html = self._get_html(
77
+ message, severity, runs, custom_html, alert, event_data
78
+ )
79
+ self.params["body"] = runs_html
80
+
81
+ if message_body_override:
82
+ self.params["body"] = message_body_override.replace(
83
+ "{{ runs }}", runs_html
84
+ ).replace("{{runs}}", runs_html)
85
+
75
86
  await self._send_email(**self.params)
76
87
 
77
88
  @classmethod
@@ -146,11 +157,11 @@ class MailNotification(base.NotificationBase):
146
157
  **kwargs,
147
158
  ):
148
159
  # Create the email message
149
- message = EmailMessage()
160
+ message = MIMEMultipart("alternative")
150
161
  message["From"] = sender_address
151
162
  message["To"] = email_addresses
152
163
  message["Subject"] = subject
153
- message.set_content(body)
164
+ message.attach(MIMEText(body, "html"))
154
165
 
155
166
  # Send the email
156
167
  await aiosmtplib.send(
@@ -21,7 +21,7 @@ import typing
21
21
  from concurrent.futures import ThreadPoolExecutor
22
22
 
23
23
  import mlrun.common.constants as mlrun_constants
24
- import mlrun.common.runtimes.constants
24
+ import mlrun.common.runtimes.constants as runtimes_constants
25
25
  import mlrun.common.schemas
26
26
  import mlrun.config
27
27
  import mlrun.db.base
@@ -226,7 +226,7 @@ class NotificationPusher(_NotificationPusherBase):
226
226
  for when_state in when_states:
227
227
  if when_state == run_state:
228
228
  if (
229
- run_state == "completed"
229
+ run_state == runtimes_constants.RunStates.completed
230
230
  and evaluate_condition_in_separate_process(
231
231
  notification.condition,
232
232
  context={
@@ -234,7 +234,11 @@ class NotificationPusher(_NotificationPusherBase):
234
234
  "notification": notification.to_dict(),
235
235
  },
236
236
  )
237
- ) or run_state in ["error", "aborted"]:
237
+ ) or run_state in [
238
+ runtimes_constants.RunStates.error,
239
+ runtimes_constants.RunStates.aborted,
240
+ runtimes_constants.RunStates.running,
241
+ ]:
238
242
  return True
239
243
 
240
244
  return False
@@ -441,7 +445,7 @@ class NotificationPusher(_NotificationPusherBase):
441
445
  _run["step_kind"] = _step.step_type
442
446
  if _step.skipped:
443
447
  _run.setdefault("status", {})["state"] = (
444
- mlrun.common.runtimes.constants.RunStates.skipped
448
+ runtimes_constants.RunStates.skipped
445
449
  )
446
450
  steps.append(_run)
447
451
 
@@ -468,7 +472,7 @@ class NotificationPusher(_NotificationPusherBase):
468
472
  if _step.skipped:
469
473
  state = mlrun.common.schemas.FunctionState.skipped
470
474
  else:
471
- state = mlrun.common.runtimes.constants.PodPhases.pod_phase_to_run_state(
475
+ state = runtimes_constants.PodPhases.pod_phase_to_run_state(
472
476
  pod_phase
473
477
  )
474
478
  function["status"] = {"state": state}
@@ -1,4 +1,4 @@
1
1
  {
2
- "git_commit": "cb94f845e0a3601e06c7e22381391d82bb386160",
3
- "version": "1.8.0-rc5"
2
+ "git_commit": "052529570e5bbae67e99d98a89713d70c6751607",
3
+ "version": "1.8.0-rc7"
4
4
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mlrun
3
- Version: 1.8.0rc5
3
+ Version: 1.8.0rc7
4
4
  Summary: Tracking and config of machine learning runs
5
5
  Home-page: https://github.com/mlrun/mlrun
6
6
  Author: Yaron Haviv
@@ -52,7 +52,7 @@ Requires-Dist: deprecated~=1.2
52
52
  Requires-Dist: jinja2>=3.1.3,~=3.1
53
53
  Requires-Dist: orjson<4,>=3.9.15
54
54
  Requires-Dist: mlrun-pipelines-kfp-common~=0.2.3
55
- Requires-Dist: mlrun-pipelines-kfp-v1-8~=0.2.2; python_version < "3.11"
55
+ Requires-Dist: mlrun-pipelines-kfp-v1-8~=0.2.3; python_version < "3.11"
56
56
  Requires-Dist: docstring_parser~=0.16
57
57
  Requires-Dist: aiosmtplib~=3.0
58
58
  Provides-Extra: s3