mlrun 1.8.0rc19__py3-none-any.whl → 1.8.0rc26__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 (52) hide show
  1. mlrun/__init__.py +37 -3
  2. mlrun/__main__.py +5 -0
  3. mlrun/alerts/alert.py +1 -0
  4. mlrun/artifacts/document.py +78 -36
  5. mlrun/common/formatters/feature_set.py +1 -0
  6. mlrun/common/runtimes/constants.py +17 -0
  7. mlrun/common/schemas/alert.py +3 -0
  8. mlrun/common/schemas/client_spec.py +0 -1
  9. mlrun/common/schemas/model_monitoring/constants.py +32 -9
  10. mlrun/common/schemas/model_monitoring/model_endpoints.py +2 -0
  11. mlrun/common/schemas/workflow.py +1 -0
  12. mlrun/config.py +39 -6
  13. mlrun/datastore/datastore_profile.py +58 -16
  14. mlrun/datastore/sources.py +7 -1
  15. mlrun/datastore/vectorstore.py +20 -1
  16. mlrun/db/base.py +20 -0
  17. mlrun/db/httpdb.py +97 -10
  18. mlrun/db/nopdb.py +19 -0
  19. mlrun/errors.py +4 -0
  20. mlrun/execution.py +15 -6
  21. mlrun/frameworks/_common/model_handler.py +0 -2
  22. mlrun/launcher/client.py +2 -2
  23. mlrun/launcher/local.py +5 -1
  24. mlrun/model_monitoring/applications/_application_steps.py +3 -1
  25. mlrun/model_monitoring/controller.py +266 -103
  26. mlrun/model_monitoring/db/tsdb/__init__.py +11 -23
  27. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +2 -0
  28. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +20 -21
  29. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -34
  30. mlrun/model_monitoring/helpers.py +16 -10
  31. mlrun/model_monitoring/stream_processing.py +106 -35
  32. mlrun/package/context_handler.py +1 -1
  33. mlrun/package/packagers_manager.py +4 -18
  34. mlrun/projects/pipelines.py +18 -5
  35. mlrun/projects/project.py +156 -39
  36. mlrun/runtimes/nuclio/serving.py +22 -13
  37. mlrun/runtimes/sparkjob/spark3job.py +1 -1
  38. mlrun/secrets.py +1 -1
  39. mlrun/serving/server.py +11 -3
  40. mlrun/serving/states.py +65 -8
  41. mlrun/serving/v2_serving.py +67 -44
  42. mlrun/utils/helpers.py +111 -23
  43. mlrun/utils/notifications/notification/base.py +6 -1
  44. mlrun/utils/notifications/notification/slack.py +5 -1
  45. mlrun/utils/notifications/notification_pusher.py +67 -36
  46. mlrun/utils/version/version.json +2 -2
  47. {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc26.dist-info}/METADATA +33 -16
  48. {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc26.dist-info}/RECORD +52 -52
  49. {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc26.dist-info}/WHEEL +1 -1
  50. {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc26.dist-info}/LICENSE +0 -0
  51. {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc26.dist-info}/entry_points.txt +0 -0
  52. {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc26.dist-info}/top_level.txt +0 -0
@@ -1089,9 +1089,10 @@ class KafkaSource(OnlineSource):
1089
1089
  attributes["partitions"] = partitions
1090
1090
  sasl = attributes.pop("sasl", {})
1091
1091
  if sasl_user and sasl_pass:
1092
- sasl["enabled"] = True
1092
+ sasl["enable"] = True
1093
1093
  sasl["user"] = sasl_user
1094
1094
  sasl["password"] = sasl_pass
1095
+ sasl["mechanism"] = "PLAIN"
1095
1096
  if sasl:
1096
1097
  attributes["sasl"] = sasl
1097
1098
  super().__init__(attributes=attributes, **kwargs)
@@ -1127,8 +1128,13 @@ class KafkaSource(OnlineSource):
1127
1128
  extra_attributes["workerAllocationMode"] = extra_attributes.get(
1128
1129
  "worker_allocation_mode", "static"
1129
1130
  )
1131
+ else:
1132
+ extra_attributes["workerAllocationMode"] = extra_attributes.get(
1133
+ "worker_allocation_mode", "pool"
1134
+ )
1130
1135
 
1131
1136
  trigger_kwargs = {}
1137
+
1132
1138
  if "max_workers" in extra_attributes:
1133
1139
  trigger_kwargs = {"max_workers": extra_attributes.pop("max_workers")}
1134
1140
 
@@ -48,6 +48,18 @@ def _extract_collection_name(vectorstore: "VectorStore") -> str: # noqa: F821
48
48
  else:
49
49
  return getattr(obj, pattern, None)
50
50
 
51
+ if type(vectorstore).__name__ == "PineconeVectorStore":
52
+ try:
53
+ url = (
54
+ vectorstore._index.config.host
55
+ if hasattr(vectorstore._index, "config")
56
+ else vectorstore._index._config.host
57
+ )
58
+ index_name = url.split("//")[1].split("-")[0]
59
+ return index_name
60
+ except Exception:
61
+ pass
62
+
51
63
  for pattern in patterns:
52
64
  try:
53
65
  value = resolve_attribute(vectorstore, pattern)
@@ -254,7 +266,14 @@ class VectorStoreCollection:
254
266
  elif store_class == "chroma":
255
267
  where = {DocumentArtifact.METADATA_SOURCE_KEY: artifact.get_source()}
256
268
  self._collection_impl.delete(where=where)
257
-
269
+ elif store_class == "pineconevectorstore":
270
+ filter = {
271
+ DocumentArtifact.METADATA_SOURCE_KEY: {"$eq": artifact.get_source()}
272
+ }
273
+ self._collection_impl.delete(filter=filter)
274
+ elif store_class == "mongodbatlasvectorsearch":
275
+ filter = {DocumentArtifact.METADATA_SOURCE_KEY: artifact.get_source()}
276
+ self._collection_impl.collection.delete_many(filter=filter)
258
277
  elif (
259
278
  hasattr(self._collection_impl, "delete")
260
279
  and "filter"
mlrun/db/base.py CHANGED
@@ -68,6 +68,18 @@ class RunDBInterface(ABC):
68
68
  ):
69
69
  pass
70
70
 
71
+ def refresh_smtp_configuration(self):
72
+ pass
73
+
74
+ def push_pipeline_notifications(
75
+ self,
76
+ pipeline_id,
77
+ project="",
78
+ notifications=None,
79
+ timeout=45,
80
+ ):
81
+ pass
82
+
71
83
  @abstractmethod
72
84
  def read_run(
73
85
  self,
@@ -917,6 +929,14 @@ class RunDBInterface(ABC):
917
929
  ):
918
930
  pass
919
931
 
932
+ @abstractmethod
933
+ def get_alert_activation(
934
+ self,
935
+ project,
936
+ activation_id,
937
+ ) -> mlrun.common.schemas.AlertActivation:
938
+ pass
939
+
920
940
  def update_alert_activation(
921
941
  self,
922
942
  activation_id: int,
mlrun/db/httpdb.py CHANGED
@@ -559,14 +559,6 @@ class HTTPRunDB(RunDBInterface):
559
559
  server_cfg.get("external_platform_tracking")
560
560
  or config.external_platform_tracking
561
561
  )
562
- config.model_endpoint_monitoring.tsdb_connection = (
563
- server_cfg.get("model_monitoring_tsdb_connection")
564
- or config.model_endpoint_monitoring.tsdb_connection
565
- )
566
- config.model_endpoint_monitoring.stream_connection = (
567
- server_cfg.get("stream_connection")
568
- or config.model_endpoint_monitoring.stream_connection
569
- )
570
562
  config.packagers = server_cfg.get("packagers") or config.packagers
571
563
  server_data_prefixes = server_cfg.get("feature_store_data_prefixes") or {}
572
564
  for prefix in ["default", "nosql", "redisnosql"]:
@@ -771,7 +763,6 @@ class HTTPRunDB(RunDBInterface):
771
763
  :returns: :py:class:`~mlrun.common.schemas.BackgroundTask`.
772
764
  """
773
765
  project = project or config.default_project
774
-
775
766
  response = self.api_call(
776
767
  "POST",
777
768
  path=f"projects/{project}/runs/{uid}/push-notifications",
@@ -780,9 +771,84 @@ class HTTPRunDB(RunDBInterface):
780
771
  )
781
772
  if response.status_code == http.HTTPStatus.ACCEPTED:
782
773
  background_task = mlrun.common.schemas.BackgroundTask(**response.json())
783
- return self._wait_for_background_task_to_reach_terminal_state(
774
+ background_task = self._wait_for_background_task_to_reach_terminal_state(
784
775
  background_task.metadata.name, project=project
785
776
  )
777
+ if (
778
+ background_task.status.state
779
+ == mlrun.common.schemas.BackgroundTaskState.succeeded
780
+ ):
781
+ logger.info(
782
+ "Notifications for the run have been pushed",
783
+ project=project,
784
+ run_id=uid,
785
+ )
786
+ elif (
787
+ background_task.status.state
788
+ == mlrun.common.schemas.BackgroundTaskState.failed
789
+ ):
790
+ logger.error(
791
+ "Failed to push run notifications",
792
+ project=project,
793
+ run_id=uid,
794
+ error=background_task.status.error,
795
+ )
796
+ return None
797
+
798
+ def push_pipeline_notifications(
799
+ self,
800
+ pipeline_id,
801
+ project="",
802
+ notifications=None,
803
+ timeout=45,
804
+ ):
805
+ """
806
+ Push notifications for a pipeline.
807
+
808
+ :param pipeline_id: Unique ID of the pipeline(KFP).
809
+ :param project: Project that the run belongs to.
810
+ :param notifications: List of notifications to push.
811
+ :returns: :py:class:`~mlrun.common.schemas.BackgroundTask`.
812
+ """
813
+ if notifications is None or type(notifications) is not list:
814
+ raise MLRunInvalidArgumentError(
815
+ "The 'notifications' parameter must be a list."
816
+ )
817
+
818
+ project = project or config.default_project
819
+
820
+ response = self.api_call(
821
+ "POST",
822
+ path=f"projects/{project}/pipelines/{pipeline_id}/push-notifications",
823
+ error="Failed push notifications",
824
+ body=_as_json([notification.to_dict() for notification in notifications]),
825
+ timeout=timeout,
826
+ )
827
+ if response.status_code == http.HTTPStatus.ACCEPTED:
828
+ background_task = mlrun.common.schemas.BackgroundTask(**response.json())
829
+ background_task = self._wait_for_background_task_to_reach_terminal_state(
830
+ background_task.metadata.name, project=project
831
+ )
832
+ if (
833
+ background_task.status.state
834
+ == mlrun.common.schemas.BackgroundTaskState.succeeded
835
+ ):
836
+ logger.info(
837
+ "Pipeline notifications have been pushed",
838
+ project=project,
839
+ pipeline_id=pipeline_id,
840
+ )
841
+ elif (
842
+ background_task.status.state
843
+ == mlrun.common.schemas.BackgroundTaskState.failed
844
+ ):
845
+ logger.error(
846
+ "Failed to push pipeline notifications",
847
+ project=project,
848
+ pipeline_id=pipeline_id,
849
+ error=background_task.status.error,
850
+ )
851
+
786
852
  return None
787
853
 
788
854
  def read_run(
@@ -4955,6 +5021,27 @@ class HTTPRunDB(RunDBInterface):
4955
5021
  **kwargs,
4956
5022
  )
4957
5023
 
5024
+ def get_alert_activation(
5025
+ self,
5026
+ project,
5027
+ activation_id,
5028
+ ) -> mlrun.common.schemas.AlertActivation:
5029
+ """
5030
+ Retrieve the alert activation by id
5031
+
5032
+ :param project: Project name for which the summary belongs.
5033
+ :param activation_id: alert activation id.
5034
+ :returns: alert activation object.
5035
+ """
5036
+ project = project or config.default_project
5037
+
5038
+ error = "get alert activation"
5039
+ path = f"projects/{project}/alert-activations/{activation_id}"
5040
+
5041
+ response = self.api_call("GET", path, error)
5042
+
5043
+ return mlrun.common.schemas.AlertActivation(**response.json())
5044
+
4958
5045
  def get_project_summary(
4959
5046
  self, project: Optional[str] = None
4960
5047
  ) -> mlrun.common.schemas.ProjectSummary:
mlrun/db/nopdb.py CHANGED
@@ -84,6 +84,18 @@ class NopDB(RunDBInterface):
84
84
  ):
85
85
  pass
86
86
 
87
+ def refresh_smtp_configuration(self):
88
+ pass
89
+
90
+ def push_pipeline_notifications(
91
+ self,
92
+ pipeline_id,
93
+ project="",
94
+ notifications=None,
95
+ timeout=45,
96
+ ):
97
+ pass
98
+
87
99
  def list_runtime_resources(
88
100
  self,
89
101
  project: Optional[str] = None,
@@ -936,5 +948,12 @@ class NopDB(RunDBInterface):
936
948
  ):
937
949
  pass
938
950
 
951
+ def get_alert_activation(
952
+ self,
953
+ project,
954
+ activation_id,
955
+ ) -> mlrun.common.schemas.AlertActivation:
956
+ pass
957
+
939
958
  def get_project_summary(self, project: str):
940
959
  pass
mlrun/errors.py CHANGED
@@ -174,6 +174,10 @@ class MLRunInvalidArgumentError(MLRunHTTPStatusError, ValueError):
174
174
  error_status_code = HTTPStatus.BAD_REQUEST.value
175
175
 
176
176
 
177
+ class MLRunModelLimitExceededError(MLRunHTTPStatusError, ValueError):
178
+ error_status_code = HTTPStatus.BAD_REQUEST.value
179
+
180
+
177
181
  class MLRunInvalidArgumentTypeError(MLRunHTTPStatusError, TypeError):
178
182
  error_status_code = HTTPStatus.BAD_REQUEST.value
179
183
 
mlrun/execution.py CHANGED
@@ -876,7 +876,7 @@ class MLClientCtx:
876
876
 
877
877
  def log_document(
878
878
  self,
879
- key: str,
879
+ key: str = "",
880
880
  tag: str = "",
881
881
  local_path: str = "",
882
882
  artifact_path: Optional[str] = None,
@@ -890,7 +890,8 @@ class MLClientCtx:
890
890
  """
891
891
  Log a document as an artifact.
892
892
 
893
- :param key: Artifact key
893
+ :param key: Optional artifact key. If not provided, will be derived from local_path
894
+ or target_path using DocumentArtifact.key_from_source()
894
895
  :param tag: Version tag
895
896
  :param local_path: path to the local file we upload, will also be use
896
897
  as the destination subpath (under "artifact_path")
@@ -923,7 +924,6 @@ class MLClientCtx:
923
924
  Example:
924
925
  >>> # Log a PDF document with custom loader
925
926
  >>> project.log_document(
926
- ... key="my_doc",
927
927
  ... local_path="path/to/doc.pdf",
928
928
  ... document_loader_spec=DocumentLoaderSpec(
929
929
  ... loader_class_name="langchain_community.document_loaders.PDFLoader",
@@ -932,10 +932,19 @@ class MLClientCtx:
932
932
  ... ),
933
933
  ... )
934
934
  """
935
+
936
+ if not key and not local_path and not target_path:
937
+ raise ValueError(
938
+ "Must provide either 'key' parameter or 'local_path'/'target_path' to derive the key from"
939
+ )
940
+ if not key:
941
+ key = DocumentArtifact.key_from_source(local_path or target_path)
942
+
935
943
  doc_artifact = DocumentArtifact(
936
944
  key=key,
937
945
  original_source=local_path or target_path,
938
946
  document_loader_spec=document_loader_spec,
947
+ collections=kwargs.pop("collections", None),
939
948
  **kwargs,
940
949
  )
941
950
 
@@ -964,12 +973,12 @@ class MLClientCtx:
964
973
  def get_artifact(
965
974
  self, key, tag=None, iter=None, tree=None, uid=None
966
975
  ) -> Optional[Artifact]:
967
- if tag or iter or tree or uid:
976
+ cached_artifact_uri = self._artifacts_manager.artifact_uris.get(key, None)
977
+ if tag or iter or tree or uid or (not cached_artifact_uri):
968
978
  project = self.get_project_object()
969
979
  return project.get_artifact(key=key, tag=tag, iter=iter, tree=tree, uid=uid)
970
980
  else:
971
- artifact_uri = self._artifacts_manager.artifact_uris[key]
972
- return self.get_store_resource(artifact_uri)
981
+ return self.get_store_resource(cached_artifact_uri)
973
982
 
974
983
  def update_artifact(self, artifact_object: Artifact):
975
984
  """Update an artifact object in the DB and the cached uri"""
@@ -976,7 +976,6 @@ class ModelHandler(ABC, Generic[CommonTypes.ModelType, CommonTypes.IOSampleType]
976
976
  custom_objects_map_json,
977
977
  local_path=custom_objects_map_json,
978
978
  artifact_path=self._context.artifact_path,
979
- db_key=False,
980
979
  )
981
980
 
982
981
  # Zip the custom objects directory:
@@ -997,7 +996,6 @@ class ModelHandler(ABC, Generic[CommonTypes.ModelType, CommonTypes.IOSampleType]
997
996
  custom_objects_zip,
998
997
  local_path=custom_objects_zip,
999
998
  artifact_path=self._context.artifact_path,
1000
- db_key=False,
1001
999
  )
1002
1000
 
1003
1001
  return artifacts
mlrun/launcher/client.py CHANGED
@@ -134,7 +134,7 @@ class ClientBaseLauncher(launcher.BaseLauncher, abc.ABC):
134
134
  if mlrun.utils.is_jupyter and mlrun.mlconf.ipython_widget:
135
135
  results_tbl.show()
136
136
  print()
137
- ui_url = mlrun.utils.get_ui_url(project, uid)
137
+ ui_url = mlrun.utils.get_run_url(project, uid=uid, name=run.metadata.name)
138
138
  if ui_url:
139
139
  ui_url = f' or <a href="{ui_url}" target="_blank">click here</a> to open in UI'
140
140
  IPython.display.display(
@@ -150,6 +150,6 @@ class ClientBaseLauncher(launcher.BaseLauncher, abc.ABC):
150
150
  mlrun.utils.logger.info(
151
151
  "To track results use the CLI", info_cmd=info_cmd, logs_cmd=logs_cmd
152
152
  )
153
- ui_url = mlrun.utils.get_ui_url(project, uid)
153
+ ui_url = mlrun.utils.get_run_url(project, uid=uid, name=run.metadata.name)
154
154
  if ui_url:
155
155
  mlrun.utils.logger.info("Or click for UI", ui_url=ui_url)
mlrun/launcher/local.py CHANGED
@@ -281,5 +281,9 @@ class ClientLocalLauncher(launcher.ClientBaseLauncher):
281
281
  # once the run is completed, and we can just push the notifications.
282
282
  # Only push from jupyter, not from the CLI.
283
283
  # "handler" and "dask" kinds are special cases of local runs which don't set local=True
284
- if self._is_run_local or runtime.kind in ["handler", "dask"]:
284
+ if self._is_run_local or runtime.kind in ["handler"]:
285
285
  mlrun.utils.notifications.NotificationPusher([runobj]).push()
286
+ elif runtime.kind in ["dask"]:
287
+ runtime._get_db().push_run_notifications(
288
+ uid=runobj.metadata.uid, project=runobj.metadata.project
289
+ )
@@ -166,7 +166,9 @@ class _ApplicationErrorHandler(StepToDict):
166
166
  "Endpoint ID": event.body.endpoint_id,
167
167
  "Application Class": event.body.application_name,
168
168
  "Error": "".join(
169
- traceback.format_exception(None, event.error, event.error.__traceback__)
169
+ traceback.format_exception(
170
+ None, value=event.error, tb=event.error.__traceback__
171
+ )
170
172
  ),
171
173
  "Timestamp": event.timestamp,
172
174
  }