mlrun 1.7.0rc43__py3-none-any.whl → 1.7.0rc55__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 (68) hide show
  1. mlrun/__main__.py +4 -2
  2. mlrun/artifacts/manager.py +3 -1
  3. mlrun/common/formatters/__init__.py +1 -0
  4. mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
  5. mlrun/common/formatters/run.py +3 -0
  6. mlrun/common/schemas/__init__.py +1 -0
  7. mlrun/common/schemas/alert.py +11 -11
  8. mlrun/common/schemas/auth.py +5 -0
  9. mlrun/common/schemas/client_spec.py +0 -1
  10. mlrun/common/schemas/model_monitoring/__init__.py +2 -1
  11. mlrun/common/schemas/model_monitoring/constants.py +23 -9
  12. mlrun/common/schemas/model_monitoring/model_endpoints.py +24 -47
  13. mlrun/common/schemas/notification.py +12 -2
  14. mlrun/common/schemas/workflow.py +10 -2
  15. mlrun/config.py +28 -21
  16. mlrun/data_types/data_types.py +6 -1
  17. mlrun/datastore/base.py +4 -4
  18. mlrun/datastore/s3.py +12 -9
  19. mlrun/datastore/storeytargets.py +9 -6
  20. mlrun/db/base.py +3 -0
  21. mlrun/db/httpdb.py +28 -16
  22. mlrun/db/nopdb.py +24 -4
  23. mlrun/errors.py +7 -1
  24. mlrun/execution.py +40 -7
  25. mlrun/feature_store/api.py +1 -0
  26. mlrun/feature_store/retrieval/spark_merger.py +7 -7
  27. mlrun/frameworks/_common/plan.py +3 -3
  28. mlrun/frameworks/_ml_common/plan.py +1 -1
  29. mlrun/frameworks/parallel_coordinates.py +2 -3
  30. mlrun/launcher/client.py +6 -6
  31. mlrun/model.py +29 -0
  32. mlrun/model_monitoring/api.py +1 -12
  33. mlrun/model_monitoring/applications/__init__.py +1 -2
  34. mlrun/model_monitoring/applications/_application_steps.py +5 -1
  35. mlrun/model_monitoring/applications/base.py +2 -182
  36. mlrun/model_monitoring/applications/context.py +2 -9
  37. mlrun/model_monitoring/applications/evidently_base.py +0 -74
  38. mlrun/model_monitoring/applications/histogram_data_drift.py +2 -2
  39. mlrun/model_monitoring/applications/results.py +4 -4
  40. mlrun/model_monitoring/controller.py +46 -209
  41. mlrun/model_monitoring/db/stores/base/store.py +1 -0
  42. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +15 -1
  43. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +12 -0
  44. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +17 -16
  45. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +49 -39
  46. mlrun/model_monitoring/helpers.py +13 -15
  47. mlrun/model_monitoring/writer.py +3 -1
  48. mlrun/projects/operations.py +11 -8
  49. mlrun/projects/pipelines.py +35 -16
  50. mlrun/projects/project.py +52 -24
  51. mlrun/render.py +3 -3
  52. mlrun/runtimes/daskjob.py +1 -1
  53. mlrun/runtimes/kubejob.py +6 -6
  54. mlrun/runtimes/nuclio/api_gateway.py +12 -0
  55. mlrun/runtimes/nuclio/application/application.py +3 -3
  56. mlrun/runtimes/nuclio/function.py +41 -0
  57. mlrun/runtimes/nuclio/serving.py +2 -2
  58. mlrun/runtimes/pod.py +19 -13
  59. mlrun/serving/server.py +2 -0
  60. mlrun/utils/helpers.py +62 -16
  61. mlrun/utils/version/version.json +2 -2
  62. {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +126 -44
  63. {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +67 -68
  64. {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
  65. mlrun/model_monitoring/evidently_application.py +0 -20
  66. {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
  67. {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
  68. {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
mlrun/__main__.py CHANGED
@@ -734,9 +734,11 @@ def get(kind, name, selector, namespace, uid, project, tag, db, extra_args):
734
734
  if db:
735
735
  mlconf.dbpath = db
736
736
  if not project:
737
- print("Warning, project parameter was not specified using default !")
737
+ logger.warning(
738
+ "Project parameter was not specified. Defaulting to 'default' project"
739
+ )
738
740
  if kind.startswith("po"):
739
- print("Unsupported, use 'get runtimes' instead")
741
+ logger.warning("Unsupported, use 'get runtimes' instead")
740
742
  return
741
743
 
742
744
  elif kind.startswith("runtime"):
@@ -200,7 +200,9 @@ class ArtifactManager:
200
200
  :param artifact_path: The path to store the artifact.
201
201
  If not provided, the artifact will be stored in the default artifact path.
202
202
  :param format: The format of the artifact. (e.g. csv, json, html, etc.)
203
- :param upload: Whether to upload the artifact or not.
203
+ :param upload: Whether to upload the artifact to the datastore. If not provided, and the
204
+ `local_path` is not a directory, upload occurs by default. Directories are uploaded only when this
205
+ flag is explicitly set to `True`.
204
206
  :param labels: Labels to add to the artifact.
205
207
  :param db_key: The key to use when logging the artifact to the DB.
206
208
  If not provided, will generate a key based on the producer name and the artifact key.
@@ -18,3 +18,4 @@ from .function import FunctionFormat # noqa
18
18
  from .pipeline import PipelineFormat # noqa
19
19
  from .project import ProjectFormat # noqa
20
20
  from .run import RunFormat # noqa
21
+ from .feature_set import FeatureSetFormat # noqa
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Iguazio
1
+ # Copyright 2024 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,9 +11,23 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ #
15
+
16
+ import typing
17
+
18
+ import mlrun.common.types
19
+
20
+ from .base import ObjectFormat
21
+
22
+
23
+ class FeatureSetFormat(ObjectFormat, mlrun.common.types.StrEnum):
24
+ minimal = "minimal"
14
25
 
15
- # TODO : delete this file in 1.9.0
16
- from mlrun.model_monitoring.applications import ( # noqa: F401
17
- ModelMonitoringApplicationBase,
18
- ModelMonitoringApplicationResult,
19
- )
26
+ @staticmethod
27
+ def format_method(_format: str) -> typing.Optional[typing.Callable]:
28
+ return {
29
+ FeatureSetFormat.full: None,
30
+ FeatureSetFormat.minimal: FeatureSetFormat.filter_obj_method(
31
+ ["kind", "metadata", "spec", "status.state"]
32
+ ),
33
+ }[_format]
@@ -22,5 +22,8 @@ class RunFormat(ObjectFormat, mlrun.common.types.StrEnum):
22
22
  # No enrichment, data is pulled as-is from the database.
23
23
  standard = "standard"
24
24
 
25
+ # Enrich run with full notifications since the notification params are subtracted from the run body.
26
+ notifications = "notifications"
27
+
25
28
  # Performs run enrichment, including the run's artifacts. Only available for the `get` run API.
26
29
  full = "full"
@@ -175,6 +175,7 @@ from .project import (
175
175
  ProjectOwner,
176
176
  ProjectsOutput,
177
177
  ProjectSpec,
178
+ ProjectSpecOut,
178
179
  ProjectState,
179
180
  ProjectStatus,
180
181
  ProjectSummariesOutput,
@@ -34,17 +34,17 @@ class EventEntities(pydantic.BaseModel):
34
34
 
35
35
 
36
36
  class EventKind(StrEnum):
37
- DATA_DRIFT_DETECTED = "data_drift_detected"
38
- DATA_DRIFT_SUSPECTED = "data_drift_suspected"
39
- CONCEPT_DRIFT_DETECTED = "concept_drift_detected"
40
- CONCEPT_DRIFT_SUSPECTED = "concept_drift_suspected"
41
- MODEL_PERFORMANCE_DETECTED = "model_performance_detected"
42
- MODEL_PERFORMANCE_SUSPECTED = "model_performance_suspected"
43
- SYSTEM_PERFORMANCE_DETECTED = "system_performance_detected"
44
- SYSTEM_PERFORMANCE_SUSPECTED = "system_performance_suspected"
45
- MM_APP_ANOMALY_DETECTED = "mm_app_anomaly_detected"
46
- MM_APP_ANOMALY_SUSPECTED = "mm_app_anomaly_suspected"
47
- MM_APP_FAILED = "mm_app_failed"
37
+ DATA_DRIFT_DETECTED = "data-drift-detected"
38
+ DATA_DRIFT_SUSPECTED = "data-drift-suspected"
39
+ CONCEPT_DRIFT_DETECTED = "concept-drift-detected"
40
+ CONCEPT_DRIFT_SUSPECTED = "concept-drift-suspected"
41
+ MODEL_PERFORMANCE_DETECTED = "model-performance-detected"
42
+ MODEL_PERFORMANCE_SUSPECTED = "model-performance-suspected"
43
+ SYSTEM_PERFORMANCE_DETECTED = "system-performance-detected"
44
+ SYSTEM_PERFORMANCE_SUSPECTED = "system-performance-suspected"
45
+ MM_APP_ANOMALY_DETECTED = "mm-app-anomaly-detected"
46
+ MM_APP_ANOMALY_SUSPECTED = "mm-app-anomaly-suspected"
47
+ MM_APP_FAILED = "mm-app-failed"
48
48
  FAILED = "failed"
49
49
 
50
50
 
@@ -63,6 +63,7 @@ class AuthorizationResourceTypes(mlrun.common.types.StrEnum):
63
63
  event = "event"
64
64
  datastore_profile = "datastore-profile"
65
65
  api_gateway = "api-gateway"
66
+ project_summaries = "project-summaries"
66
67
 
67
68
  def to_resource_string(
68
69
  self,
@@ -72,6 +73,7 @@ class AuthorizationResourceTypes(mlrun.common.types.StrEnum):
72
73
  return {
73
74
  # project is the resource itself, so no need for both resource_name and project_name
74
75
  AuthorizationResourceTypes.project: "/projects/{project_name}",
76
+ AuthorizationResourceTypes.project_summaries: "/projects/{project_name}/project-summaries/{resource_name}",
75
77
  AuthorizationResourceTypes.function: "/projects/{project_name}/functions/{resource_name}",
76
78
  AuthorizationResourceTypes.artifact: "/projects/{project_name}/artifacts/{resource_name}",
77
79
  AuthorizationResourceTypes.project_background_task: (
@@ -139,6 +141,9 @@ class AuthInfo(pydantic.BaseModel):
139
141
  member_ids.extend(self.user_group_ids)
140
142
  return member_ids
141
143
 
144
+ def get_session(self) -> str:
145
+ return self.data_session or self.session
146
+
142
147
 
143
148
  class Credentials(pydantic.BaseModel):
144
149
  access_key: typing.Optional[str]
@@ -57,7 +57,6 @@ class ClientSpec(pydantic.BaseModel):
57
57
  redis_url: typing.Optional[str]
58
58
  redis_type: typing.Optional[str]
59
59
  sql_url: typing.Optional[str]
60
- model_endpoint_monitoring_store_type: typing.Optional[str]
61
60
  model_endpoint_monitoring_endpoint_store_connection: typing.Optional[str]
62
61
  model_monitoring_tsdb_connection: typing.Optional[str]
63
62
  ce: typing.Optional[dict]
@@ -25,6 +25,7 @@ from .constants import (
25
25
  FileTargetKind,
26
26
  FunctionURI,
27
27
  MetricData,
28
+ ModelEndpointMonitoringMetricType,
28
29
  ModelEndpointTarget,
29
30
  ModelEndpointTargetSchemas,
30
31
  ModelMonitoringMode,
@@ -34,6 +35,7 @@ from .constants import (
34
35
  ProjectSecretKeys,
35
36
  ResultData,
36
37
  ResultKindApp,
38
+ ResultStatusApp,
37
39
  SchedulingKeys,
38
40
  SpecialApps,
39
41
  TDEngineSuperTables,
@@ -60,7 +62,6 @@ from .model_endpoints import (
60
62
  ModelEndpointMetadata,
61
63
  ModelEndpointMonitoringMetric,
62
64
  ModelEndpointMonitoringMetricNoData,
63
- ModelEndpointMonitoringMetricType,
64
65
  ModelEndpointMonitoringMetricValues,
65
66
  ModelEndpointMonitoringResultValues,
66
67
  ModelEndpointSpec,
@@ -13,7 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import hashlib
16
- from dataclasses import dataclass
16
+ import re
17
+ from dataclasses import dataclass, field
17
18
  from enum import Enum, IntEnum
18
19
  from typing import Optional
19
20
 
@@ -104,15 +105,8 @@ class ApplicationEvent:
104
105
  APPLICATION_NAME = "application_name"
105
106
  START_INFER_TIME = "start_infer_time"
106
107
  END_INFER_TIME = "end_infer_time"
107
- LAST_REQUEST = "last_request"
108
108
  ENDPOINT_ID = "endpoint_id"
109
109
  OUTPUT_STREAM_URI = "output_stream_uri"
110
- MLRUN_CONTEXT = "mlrun_context"
111
-
112
- # Deprecated fields - TODO : delete in 1.9.0 (V1 app deprecation)
113
- SAMPLE_PARQUET_PATH = "sample_parquet_path"
114
- CURRENT_STATS = "current_stats"
115
- FEATURE_STATS = "feature_stats"
116
110
 
117
111
 
118
112
  class WriterEvent(MonitoringStrEnum):
@@ -295,7 +289,7 @@ class EndpointUID:
295
289
  function_hash_key: str
296
290
  model: str
297
291
  model_version: str
298
- uid: Optional[str] = None
292
+ uid: str = field(init=False)
299
293
 
300
294
  def __post_init__(self):
301
295
  function_ref = (
@@ -372,3 +366,23 @@ _RESERVED_FUNCTION_NAMES = MonitoringFunctionNames.list() + [SpecialApps.MLRUN_I
372
366
 
373
367
 
374
368
  V3IO_MODEL_MONITORING_DB = "v3io"
369
+
370
+
371
+ class ModelEndpointMonitoringMetricType(StrEnum):
372
+ RESULT = "result"
373
+ METRIC = "metric"
374
+
375
+
376
+ _FQN_PART_PATTERN = r"[a-zA-Z0-9_-]+"
377
+ FQN_PATTERN = (
378
+ rf"^(?P<project>{_FQN_PART_PATTERN})\."
379
+ rf"(?P<app>{_FQN_PART_PATTERN})\."
380
+ rf"(?P<type>{ModelEndpointMonitoringMetricType.RESULT}|{ModelEndpointMonitoringMetricType.METRIC})\."
381
+ rf"(?P<name>{_FQN_PART_PATTERN})$"
382
+ )
383
+ FQN_REGEX = re.compile(FQN_PATTERN)
384
+
385
+ # refer to `mlrun.utils.regex.project_name`
386
+ PROJECT_PATTERN = r"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
387
+
388
+ MODEL_ENDPOINT_ID_PATTERN = r"^[a-zA-Z0-9_-]+$"
@@ -14,26 +14,32 @@
14
14
 
15
15
  import enum
16
16
  import json
17
- import re
18
17
  from datetime import datetime
19
- from typing import Any, NamedTuple, Optional
18
+ from typing import Any, NamedTuple, Optional, TypeVar
20
19
 
21
- from pydantic import BaseModel, Field, validator
22
- from pydantic.main import Extra
20
+ from pydantic import BaseModel, Extra, Field, constr, validator
23
21
 
24
- import mlrun.common.types
22
+ # TODO: remove the unused import below after `mlrun.datastore` and `mlrun.utils` usage is removed.
23
+ # At the moment `make lint` fails if this is removed.
24
+ import mlrun.common.model_monitoring
25
25
 
26
26
  from ..object import ObjectKind, ObjectSpec, ObjectStatus
27
27
  from .constants import (
28
+ FQN_REGEX,
29
+ MODEL_ENDPOINT_ID_PATTERN,
30
+ PROJECT_PATTERN,
28
31
  EndpointType,
29
32
  EventFieldType,
30
33
  EventKeyMetrics,
31
34
  EventLiveStats,
35
+ ModelEndpointMonitoringMetricType,
32
36
  ModelMonitoringMode,
33
37
  ResultKindApp,
34
38
  ResultStatusApp,
35
39
  )
36
40
 
41
+ Model = TypeVar("Model", bound=BaseModel)
42
+
37
43
 
38
44
  class ModelMonitoringStoreKinds:
39
45
  # TODO: do changes in examples & demos In 1.5.0 remove
@@ -42,9 +48,9 @@ class ModelMonitoringStoreKinds:
42
48
 
43
49
 
44
50
  class ModelEndpointMetadata(BaseModel):
45
- project: Optional[str] = ""
51
+ project: constr(regex=PROJECT_PATTERN)
52
+ uid: constr(regex=MODEL_ENDPOINT_ID_PATTERN)
46
53
  labels: Optional[dict] = {}
47
- uid: Optional[str] = ""
48
54
 
49
55
  class Config:
50
56
  extra = Extra.allow
@@ -57,12 +63,11 @@ class ModelEndpointMetadata(BaseModel):
57
63
  :param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
58
64
  dictionary using json.loads().
59
65
  """
60
- new_object = cls()
61
66
  if json_parse_values is None:
62
67
  json_parse_values = [EventFieldType.LABELS]
63
68
 
64
69
  return _mapping_attributes(
65
- base_model=new_object,
70
+ model_class=cls,
66
71
  flattened_dictionary=endpoint_dict,
67
72
  json_parse_values=json_parse_values,
68
73
  )
@@ -89,7 +94,6 @@ class ModelEndpointSpec(ObjectSpec):
89
94
  :param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
90
95
  dictionary using json.loads().
91
96
  """
92
- new_object = cls()
93
97
  if json_parse_values is None:
94
98
  json_parse_values = [
95
99
  EventFieldType.FEATURE_NAMES,
@@ -97,7 +101,7 @@ class ModelEndpointSpec(ObjectSpec):
97
101
  EventFieldType.MONITOR_CONFIGURATION,
98
102
  ]
99
103
  return _mapping_attributes(
100
- base_model=new_object,
104
+ model_class=cls,
101
105
  flattened_dictionary=endpoint_dict,
102
106
  json_parse_values=json_parse_values,
103
107
  )
@@ -191,7 +195,6 @@ class ModelEndpointStatus(ObjectStatus):
191
195
  :param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
192
196
  dictionary using json.loads().
193
197
  """
194
- new_object = cls()
195
198
  if json_parse_values is None:
196
199
  json_parse_values = [
197
200
  EventFieldType.FEATURE_STATS,
@@ -203,7 +206,7 @@ class ModelEndpointStatus(ObjectStatus):
203
206
  EventFieldType.ENDPOINT_TYPE,
204
207
  ]
205
208
  return _mapping_attributes(
206
- base_model=new_object,
209
+ model_class=cls,
207
210
  flattened_dictionary=endpoint_dict,
208
211
  json_parse_values=json_parse_values,
209
212
  )
@@ -211,22 +214,13 @@ class ModelEndpointStatus(ObjectStatus):
211
214
 
212
215
  class ModelEndpoint(BaseModel):
213
216
  kind: ObjectKind = Field(ObjectKind.model_endpoint, const=True)
214
- metadata: ModelEndpointMetadata = ModelEndpointMetadata()
217
+ metadata: ModelEndpointMetadata
215
218
  spec: ModelEndpointSpec = ModelEndpointSpec()
216
219
  status: ModelEndpointStatus = ModelEndpointStatus()
217
220
 
218
221
  class Config:
219
222
  extra = Extra.allow
220
223
 
221
- def __init__(self, **data: Any):
222
- super().__init__(**data)
223
- if self.metadata.uid is None:
224
- uid = mlrun.common.model_monitoring.create_model_endpoint_uid(
225
- function_uri=self.spec.function_uri,
226
- versioned_model=self.spec.model,
227
- )
228
- self.metadata.uid = str(uid)
229
-
230
224
  def flat_dict(self):
231
225
  """Generate a flattened `ModelEndpoint` dictionary. The flattened dictionary result is important for storing
232
226
  the model endpoint object in the database.
@@ -267,7 +261,7 @@ class ModelEndpoint(BaseModel):
267
261
  return flatten_dict
268
262
 
269
263
  @classmethod
270
- def from_flat_dict(cls, endpoint_dict: dict):
264
+ def from_flat_dict(cls, endpoint_dict: dict) -> "ModelEndpoint":
271
265
  """Create a `ModelEndpoint` object from an endpoint flattened dictionary. Because the provided dictionary
272
266
  is flattened, we pass it as is to the subclasses without splitting the keys into spec, metadata, and status.
273
267
 
@@ -285,11 +279,6 @@ class ModelEndpointList(BaseModel):
285
279
  endpoints: list[ModelEndpoint] = []
286
280
 
287
281
 
288
- class ModelEndpointMonitoringMetricType(mlrun.common.types.StrEnum):
289
- RESULT = "result"
290
- METRIC = "metric"
291
-
292
-
293
282
  class ModelEndpointMonitoringMetric(BaseModel):
294
283
  project: str
295
284
  app: str
@@ -308,18 +297,8 @@ def _compose_full_name(
308
297
  return ".".join([project, app, type, name])
309
298
 
310
299
 
311
- _FQN_PART_PATTERN = r"[a-zA-Z0-9_-]+"
312
- _FQN_PATTERN = (
313
- rf"^(?P<project>{_FQN_PART_PATTERN})\."
314
- rf"(?P<app>{_FQN_PART_PATTERN})\."
315
- rf"(?P<type>{ModelEndpointMonitoringMetricType.RESULT}|{ModelEndpointMonitoringMetricType.METRIC})\."
316
- rf"(?P<name>{_FQN_PART_PATTERN})$"
317
- )
318
- _FQN_REGEX = re.compile(_FQN_PATTERN)
319
-
320
-
321
300
  def _parse_metric_fqn_to_monitoring_metric(fqn: str) -> ModelEndpointMonitoringMetric:
322
- match = _FQN_REGEX.fullmatch(fqn)
301
+ match = FQN_REGEX.fullmatch(fqn)
323
302
  if match is None:
324
303
  raise ValueError("The fully qualified name is not in the expected format")
325
304
  return ModelEndpointMonitoringMetric.parse_obj(
@@ -364,20 +343,18 @@ class ModelEndpointMonitoringMetricNoData(_ModelEndpointMonitoringMetricValuesBa
364
343
 
365
344
 
366
345
  def _mapping_attributes(
367
- base_model: BaseModel,
368
- flattened_dictionary: dict,
369
- json_parse_values: list = None,
370
- ):
346
+ model_class: type[Model], flattened_dictionary: dict, json_parse_values: list
347
+ ) -> Model:
371
348
  """Generate a `BaseModel` object with the provided dictionary attributes.
372
349
 
373
- :param base_model: `BaseModel` object (e.g. `ModelEndpointMetadata`).
350
+ :param model_class: `BaseModel` class (e.g. `ModelEndpointMetadata`).
374
351
  :param flattened_dictionary: Flattened dictionary that contains the model endpoint attributes.
375
352
  :param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
376
353
  dictionary using json.loads().
377
354
  """
378
355
  # Get the fields of the provided base model object. These fields will be used to filter to relevent keys
379
356
  # from the flattened dictionary.
380
- wanted_keys = base_model.__fields__.keys()
357
+ wanted_keys = model_class.__fields__.keys()
381
358
 
382
359
  # Generate a filtered flattened dictionary that will be parsed into the BaseModel object
383
360
  dict_to_parse = {}
@@ -391,7 +368,7 @@ def _mapping_attributes(
391
368
  else:
392
369
  dict_to_parse[field_key] = flattened_dictionary[field_key]
393
370
 
394
- return base_model.parse_obj(dict_to_parse)
371
+ return model_class.parse_obj(dict_to_parse)
395
372
 
396
373
 
397
374
  def _json_loads_if_not_none(field: Any) -> Any:
@@ -50,9 +50,19 @@ class NotificationKind(mlrun.common.types.StrEnum):
50
50
  **url** - The webhook url to which to send the notification.\n
51
51
  **method** - The http method to use when sending the notification (GET, POST, PUT, etc…).\n
52
52
  **headers** - (dict) The http headers to send with the notification.\n
53
- **override_body** - (dict) The body to send with the notification.\n
53
+ **override_body** -
54
+ (dict) The body to send with the notification. If not specified, the
55
+ default body will be a dictionary containing `name`, `message`, `severity`, and a `runs` list of the
56
+ completed runs. You can also add the run's details.\n
57
+ Example::
58
+
59
+ "override_body": {"message":"Run Completed {{ runs }}"
60
+ # Results would look like:
61
+ "message": "Run Completed [{'project': 'my-project', 'name': 'my-function', 'host': <run-host>,
62
+ 'status': {'state': 'completed', 'results': <run-results>}}]"
54
63
  **verify_ssl** -
55
- (bool) Whether SSL certificates are validated during HTTP requests or not, The default is set to True.
64
+ (bool) Whether SSL certificates are validated during HTTP requests or not.
65
+ The default is set to True.\n
56
66
  """
57
67
 
58
68
 
@@ -16,8 +16,9 @@ import typing
16
16
 
17
17
  import pydantic
18
18
 
19
- from .notification import Notification
20
- from .schedule import ScheduleCronTrigger
19
+ from mlrun.common.schemas.notification import Notification
20
+ from mlrun.common.schemas.schedule import ScheduleCronTrigger
21
+ from mlrun.common.types import StrEnum
21
22
 
22
23
 
23
24
  class WorkflowSpec(pydantic.BaseModel):
@@ -32,6 +33,7 @@ class WorkflowSpec(pydantic.BaseModel):
32
33
  schedule: typing.Union[str, ScheduleCronTrigger] = None
33
34
  run_local: typing.Optional[bool] = None
34
35
  image: typing.Optional[str] = None
36
+ workflow_runner_node_selector: typing.Optional[dict[str, str]] = None
35
37
 
36
38
 
37
39
  class WorkflowRequest(pydantic.BaseModel):
@@ -54,3 +56,9 @@ class WorkflowResponse(pydantic.BaseModel):
54
56
 
55
57
  class GetWorkflowResponse(pydantic.BaseModel):
56
58
  workflow_id: str = None
59
+
60
+
61
+ class EngineType(StrEnum):
62
+ LOCAL = "local"
63
+ REMOTE = "remote"
64
+ KFP = "kfp"
mlrun/config.py CHANGED
@@ -539,7 +539,6 @@ default_config = {
539
539
  "store_prefixes": {
540
540
  "default": "v3io:///users/pipelines/{project}/model-endpoints/{kind}",
541
541
  "user_space": "v3io:///projects/{project}/model-endpoints/{kind}",
542
- "stream": "", # TODO: Delete in 1.9.0
543
542
  "monitoring_application": "v3io:///users/pipelines/{project}/monitoring-apps/",
544
543
  },
545
544
  # Offline storage path can be either relative or a full path. This path is used for general offline data
@@ -552,7 +551,6 @@ default_config = {
552
551
  "parquet_batching_max_events": 10_000,
553
552
  "parquet_batching_timeout_secs": timedelta(minutes=1).total_seconds(),
554
553
  # See mlrun.model_monitoring.db.stores.ObjectStoreFactory for available options
555
- "store_type": "v3io-nosql", # TODO: Delete in 1.9.0
556
554
  "endpoint_store_connection": "",
557
555
  # See mlrun.model_monitoring.db.tsdb.ObjectTSDBFactory for available options
558
556
  "tsdb_connection": "",
@@ -736,7 +734,7 @@ default_config = {
736
734
  "grafana_url": "",
737
735
  "alerts": {
738
736
  # supported modes: "enabled", "disabled".
739
- "mode": "enabled",
737
+ "mode": "disabled",
740
738
  # maximum number of alerts we allow to be configured.
741
739
  # user will get an error when exceeding this
742
740
  "max_allowed": 10000,
@@ -798,7 +796,21 @@ class Config:
798
796
  for key, value in cfg.items():
799
797
  if hasattr(self, key):
800
798
  if isinstance(value, dict):
801
- getattr(self, key).update(value)
799
+ # ignore the `skip_errors` flag here
800
+ # if the key does not align with what mlrun config expects it is a user
801
+ # input error that can lead to unexpected behavior.
802
+ # raise the exception to ensure configuration is loaded correctly and do not
803
+ # ignore any errors.
804
+ config_value = getattr(self, key)
805
+ try:
806
+ config_value.update(value)
807
+ except AttributeError as exc:
808
+ if not isinstance(config_value, (dict, Config)):
809
+ raise ValueError(
810
+ f"Can not update `{key}` config. "
811
+ f"Expected a configuration but received {type(value)}"
812
+ ) from exc
813
+ raise exc
802
814
  else:
803
815
  try:
804
816
  setattr(self, key, value)
@@ -1102,6 +1114,9 @@ class Config:
1102
1114
  # importing here to avoid circular dependency
1103
1115
  import mlrun.db
1104
1116
 
1117
+ # It ensures that SSL verification is set before establishing a connection
1118
+ _configure_ssl_verification(self.httpdb.http.verify)
1119
+
1105
1120
  # when dbpath is set we want to connect to it which will sync configuration from it to the client
1106
1121
  mlrun.db.get_run_db(value, force_reconnect=True)
1107
1122
 
@@ -1130,10 +1145,10 @@ class Config:
1130
1145
  project: str = "",
1131
1146
  kind: str = "",
1132
1147
  target: str = "online",
1133
- artifact_path: str = None,
1134
- function_name: str = None,
1148
+ artifact_path: typing.Optional[str] = None,
1149
+ function_name: typing.Optional[str] = None,
1135
1150
  **kwargs,
1136
- ) -> typing.Union[str, list[str]]:
1151
+ ) -> str:
1137
1152
  """Get the full path from the configuration based on the provided project and kind.
1138
1153
 
1139
1154
  :param project: Project name.
@@ -1149,8 +1164,7 @@ class Config:
1149
1164
  relative artifact path will be taken from the global MLRun artifact path.
1150
1165
  :param function_name: Application name, None for model_monitoring_stream.
1151
1166
 
1152
- :return: Full configured path for the provided kind. Can be either a single path
1153
- or a list of paths in the case of the online model monitoring stream path.
1167
+ :return: Full configured path for the provided kind.
1154
1168
  """
1155
1169
 
1156
1170
  if target != "offline":
@@ -1171,18 +1185,11 @@ class Config:
1171
1185
  if function_name is None
1172
1186
  else f"{kind}-{function_name.lower()}",
1173
1187
  )
1174
- elif kind == "stream": # return list for mlrun<1.6.3 BC
1175
- return [
1176
- # TODO: remove the first stream in 1.9.0
1177
- mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
1178
- project=project,
1179
- kind=kind,
1180
- ), # old stream uri (pipelines) for BC ML-6043
1181
- mlrun.mlconf.model_endpoint_monitoring.store_prefixes.user_space.format(
1182
- project=project,
1183
- kind=kind,
1184
- ), # new stream uri (projects)
1185
- ]
1188
+ elif kind == "stream":
1189
+ return mlrun.mlconf.model_endpoint_monitoring.store_prefixes.user_space.format(
1190
+ project=project,
1191
+ kind=kind,
1192
+ )
1186
1193
  else:
1187
1194
  return mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
1188
1195
  project=project,
@@ -70,6 +70,11 @@ def pa_type_to_value_type(type_):
70
70
  if isinstance(type_, TimestampType):
71
71
  return ValueType.DATETIME
72
72
 
73
+ # pandas category type translates to pyarrow DictionaryType
74
+ # we need to unpack the value type (ML-7868)
75
+ if isinstance(type_, pyarrow.DictionaryType):
76
+ type_ = type_.value_type
77
+
73
78
  type_map = {
74
79
  pyarrow.bool_(): ValueType.BOOL,
75
80
  pyarrow.int64(): ValueType.INT64,
@@ -139,7 +144,7 @@ def gbq_to_pandas_dtype(gbq_type):
139
144
  "BOOL": "bool",
140
145
  "FLOAT": "float64",
141
146
  "INTEGER": pd.Int64Dtype(),
142
- "TIMESTAMP": "datetime64[ns]",
147
+ "TIMESTAMP": "datetime64[ns, UTC]",
143
148
  }
144
149
  return type_map.get(gbq_type, "object")
145
150
 
mlrun/datastore/base.py CHANGED
@@ -29,7 +29,7 @@ from deprecated import deprecated
29
29
  import mlrun.config
30
30
  import mlrun.errors
31
31
  from mlrun.errors import err_to_str
32
- from mlrun.utils import StorePrefix, is_ipython, logger
32
+ from mlrun.utils import StorePrefix, is_jupyter, logger
33
33
 
34
34
  from .store_resources import is_store_uri, parse_store_uri
35
35
  from .utils import filter_df_start_end_time, select_columns_from_df
@@ -619,14 +619,14 @@ class DataItem:
619
619
  )
620
620
  return df
621
621
 
622
- def show(self, format=None):
622
+ def show(self, format: Optional[str] = None) -> None:
623
623
  """show the data object content in Jupyter
624
624
 
625
625
  :param format: format to use (when there is no/wrong suffix), e.g. 'png'
626
626
  """
627
- if not is_ipython:
627
+ if not is_jupyter:
628
628
  logger.warning(
629
- "Jupyter/IPython was not detected, .show() will only display inside Jupyter"
629
+ "Jupyter was not detected. `.show()` displays only inside Jupyter."
630
630
  )
631
631
  return
632
632