mlrun 1.7.0rc39__py3-none-any.whl → 1.7.0rc42__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 (58) hide show
  1. mlrun/common/constants.py +3 -0
  2. mlrun/common/db/sql_session.py +3 -2
  3. mlrun/common/helpers.py +0 -1
  4. mlrun/common/schemas/api_gateway.py +6 -6
  5. mlrun/common/schemas/common.py +4 -4
  6. mlrun/common/schemas/model_monitoring/model_endpoints.py +0 -1
  7. mlrun/config.py +1 -1
  8. mlrun/data_types/to_pandas.py +12 -12
  9. mlrun/datastore/alibaba_oss.py +1 -0
  10. mlrun/datastore/azure_blob.py +1 -6
  11. mlrun/datastore/base.py +12 -0
  12. mlrun/datastore/dbfs_store.py +1 -5
  13. mlrun/datastore/filestore.py +1 -3
  14. mlrun/datastore/google_cloud_storage.py +1 -9
  15. mlrun/datastore/redis.py +1 -0
  16. mlrun/datastore/s3.py +1 -0
  17. mlrun/datastore/storeytargets.py +147 -0
  18. mlrun/datastore/targets.py +67 -69
  19. mlrun/datastore/v3io.py +1 -0
  20. mlrun/errors.py +7 -4
  21. mlrun/feature_store/feature_vector.py +3 -1
  22. mlrun/feature_store/retrieval/job.py +3 -1
  23. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  24. mlrun/model.py +1 -1
  25. mlrun/model_monitoring/api.py +1 -2
  26. mlrun/model_monitoring/applications/_application_steps.py +25 -43
  27. mlrun/model_monitoring/applications/context.py +206 -70
  28. mlrun/model_monitoring/controller.py +0 -1
  29. mlrun/model_monitoring/db/stores/__init__.py +3 -3
  30. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +17 -8
  31. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +14 -4
  32. mlrun/model_monitoring/db/tsdb/__init__.py +3 -3
  33. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +18 -10
  34. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -23
  35. mlrun/model_monitoring/helpers.py +38 -1
  36. mlrun/model_monitoring/stream_processing.py +8 -26
  37. mlrun/package/packagers/default_packager.py +2 -2
  38. mlrun/projects/project.py +17 -16
  39. mlrun/runtimes/funcdoc.py +1 -1
  40. mlrun/runtimes/nuclio/api_gateway.py +9 -0
  41. mlrun/runtimes/nuclio/application/application.py +131 -55
  42. mlrun/runtimes/nuclio/function.py +4 -10
  43. mlrun/runtimes/nuclio/serving.py +2 -2
  44. mlrun/runtimes/sparkjob/spark3job.py +1 -1
  45. mlrun/runtimes/utils.py +16 -0
  46. mlrun/serving/routers.py +1 -1
  47. mlrun/serving/server.py +19 -5
  48. mlrun/serving/states.py +8 -0
  49. mlrun/serving/v2_serving.py +34 -26
  50. mlrun/utils/helpers.py +12 -2
  51. mlrun/utils/v3io_clients.py +2 -2
  52. mlrun/utils/version/version.json +2 -2
  53. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc42.dist-info}/METADATA +2 -2
  54. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc42.dist-info}/RECORD +58 -57
  55. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc42.dist-info}/WHEEL +1 -1
  56. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc42.dist-info}/LICENSE +0 -0
  57. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc42.dist-info}/entry_points.txt +0 -0
  58. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc42.dist-info}/top_level.txt +0 -0
@@ -24,6 +24,7 @@ import mlrun.common.model_monitoring
24
24
  import mlrun.common.schemas.model_monitoring as mm_schemas
25
25
  import mlrun.feature_store.steps
26
26
  import mlrun.utils.v3io_clients
27
+ from mlrun.common.schemas import EventFieldType
27
28
  from mlrun.model_monitoring.db import TSDBConnector
28
29
  from mlrun.model_monitoring.helpers import get_invocations_fqn
29
30
  from mlrun.utils import logger
@@ -64,14 +65,17 @@ class V3IOTSDBConnector(TSDBConnector):
64
65
  self.container = container
65
66
 
66
67
  self.v3io_framesd = v3io_framesd or mlrun.mlconf.v3io_framesd
67
- self._frames_client: v3io_frames.client.ClientBase = (
68
- self._get_v3io_frames_client(self.container)
69
- )
70
-
68
+ self._frames_client: Optional[v3io_frames.client.ClientBase] = None
71
69
  self._init_tables_path()
70
+ self._create_table = create_table
72
71
 
73
- if create_table:
74
- self.create_tables()
72
+ @property
73
+ def frames_client(self) -> v3io_frames.client.ClientBase:
74
+ if not self._frames_client:
75
+ self._frames_client = self._get_v3io_frames_client(self.container)
76
+ if self._create_table:
77
+ self.create_tables()
78
+ return self._frames_client
75
79
 
76
80
  def _init_tables_path(self):
77
81
  self.tables = {}
@@ -151,7 +155,7 @@ class V3IOTSDBConnector(TSDBConnector):
151
155
  for table_name in application_tables:
152
156
  logger.info("Creating table in V3IO TSDB", table_name=table_name)
153
157
  table = self.tables[table_name]
154
- self._frames_client.create(
158
+ self.frames_client.create(
155
159
  backend=_TSDB_BE,
156
160
  table=table,
157
161
  if_exists=v3io_frames.IGNORE,
@@ -161,8 +165,9 @@ class V3IOTSDBConnector(TSDBConnector):
161
165
  def apply_monitoring_stream_steps(
162
166
  self,
163
167
  graph,
164
- tsdb_batching_max_events: int = 10,
165
- tsdb_batching_timeout_secs: int = 300,
168
+ tsdb_batching_max_events: int = 1000,
169
+ tsdb_batching_timeout_secs: int = 30,
170
+ sample_window: int = 10,
166
171
  ):
167
172
  """
168
173
  Apply TSDB steps on the provided monitoring graph. Throughout these steps, the graph stores live data of
@@ -173,6 +178,7 @@ class V3IOTSDBConnector(TSDBConnector):
173
178
  - endpoint_features (Prediction and feature names and values)
174
179
  - custom_metrics (user-defined metrics)
175
180
  """
181
+
176
182
  # Write latency per prediction, labeled by endpoint ID only
177
183
  graph.add_step(
178
184
  "storey.TSDBTarget",
@@ -197,17 +203,23 @@ class V3IOTSDBConnector(TSDBConnector):
197
203
  key=mm_schemas.EventFieldType.ENDPOINT_ID,
198
204
  )
199
205
 
206
+ # Emits the event in window size of events based on sample_window size (10 by default)
207
+ graph.add_step(
208
+ "storey.steps.SampleWindow",
209
+ name="sample",
210
+ after="Rename",
211
+ window_size=sample_window,
212
+ key=EventFieldType.ENDPOINT_ID,
213
+ )
214
+
200
215
  # Before writing data to TSDB, create dictionary of 2-3 dictionaries that contains
201
216
  # stats and details about the events
202
217
 
203
- def apply_process_before_tsdb():
204
- graph.add_step(
205
- "mlrun.model_monitoring.db.tsdb.v3io.stream_graph_steps.ProcessBeforeTSDB",
206
- name="ProcessBeforeTSDB",
207
- after="sample",
208
- )
209
-
210
- apply_process_before_tsdb()
218
+ graph.add_step(
219
+ "mlrun.model_monitoring.db.tsdb.v3io.stream_graph_steps.ProcessBeforeTSDB",
220
+ name="ProcessBeforeTSDB",
221
+ after="sample",
222
+ )
211
223
 
212
224
  # Unpacked keys from each dictionary and write to TSDB target
213
225
  def apply_filter_and_unpacked_keys(name, keys):
@@ -273,8 +285,8 @@ class V3IOTSDBConnector(TSDBConnector):
273
285
  def handle_model_error(
274
286
  self,
275
287
  graph,
276
- tsdb_batching_max_events: int = 10,
277
- tsdb_batching_timeout_secs: int = 60,
288
+ tsdb_batching_max_events: int = 1000,
289
+ tsdb_batching_timeout_secs: int = 30,
278
290
  **kwargs,
279
291
  ) -> None:
280
292
  graph.add_step(
@@ -333,7 +345,7 @@ class V3IOTSDBConnector(TSDBConnector):
333
345
  raise ValueError(f"Invalid {kind = }")
334
346
 
335
347
  try:
336
- self._frames_client.write(
348
+ self.frames_client.write(
337
349
  backend=_TSDB_BE,
338
350
  table=table,
339
351
  dfs=pd.DataFrame.from_records([event]),
@@ -360,7 +372,7 @@ class V3IOTSDBConnector(TSDBConnector):
360
372
  tables = mm_schemas.V3IOTSDBTables.list()
361
373
  for table_to_delete in tables:
362
374
  try:
363
- self._frames_client.delete(backend=_TSDB_BE, table=table_to_delete)
375
+ self.frames_client.delete(backend=_TSDB_BE, table=table_to_delete)
364
376
  except v3io_frames.DeleteError as e:
365
377
  logger.warning(
366
378
  f"Failed to delete TSDB table '{table}'",
@@ -476,7 +488,7 @@ class V3IOTSDBConnector(TSDBConnector):
476
488
  aggregators = ",".join(agg_funcs) if agg_funcs else None
477
489
  table_path = self.tables[table]
478
490
  try:
479
- df = self._frames_client.read(
491
+ df = self.frames_client.read(
480
492
  backend=_TSDB_BE,
481
493
  table=table_path,
482
494
  start=start,
@@ -579,7 +591,7 @@ class V3IOTSDBConnector(TSDBConnector):
579
591
 
580
592
  logger.debug("Querying V3IO TSDB", query=query)
581
593
 
582
- df: pd.DataFrame = self._frames_client.read(
594
+ df: pd.DataFrame = self.frames_client.read(
583
595
  backend=_TSDB_BE,
584
596
  start=start,
585
597
  end=end,
@@ -19,9 +19,11 @@ import numpy as np
19
19
  import pandas as pd
20
20
 
21
21
  import mlrun
22
+ import mlrun.artifacts
22
23
  import mlrun.common.model_monitoring.helpers
23
24
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
24
25
  import mlrun.data_types.infer
26
+ import mlrun.model_monitoring
25
27
  from mlrun.common.schemas.model_monitoring.model_endpoints import (
26
28
  ModelEndpointMonitoringMetric,
27
29
  ModelEndpointMonitoringMetricType,
@@ -253,7 +255,7 @@ def calculate_inputs_statistics(
253
255
  )
254
256
 
255
257
  # Recalculate the histograms over the bins that are set in the sample-set of the end point:
256
- for feature in inputs_statistics.keys():
258
+ for feature in list(inputs_statistics):
257
259
  if feature in sample_set_statistics:
258
260
  counts, bins = np.histogram(
259
261
  inputs[feature].to_numpy(),
@@ -270,6 +272,9 @@ def calculate_inputs_statistics(
270
272
  inputs_statistics[feature]["hist"]
271
273
  )
272
274
  )
275
+ else:
276
+ # If the feature is not in the sample set and doesn't have a histogram, remove it from the statistics:
277
+ inputs_statistics.pop(feature)
273
278
 
274
279
  return inputs_statistics
275
280
 
@@ -322,3 +327,35 @@ def get_invocations_metric(project: str) -> ModelEndpointMonitoringMetric:
322
327
  name=mm_constants.PredictionsQueryConstants.INVOCATIONS,
323
328
  full_name=get_invocations_fqn(project),
324
329
  )
330
+
331
+
332
+ def enrich_model_endpoint_with_model_uri(
333
+ model_endpoint: ModelEndpoint,
334
+ model_obj: mlrun.artifacts.ModelArtifact,
335
+ ):
336
+ """
337
+ Enrich the model endpoint object with the model uri from the model object. We will use a unique reference
338
+ to the model object that includes the project, db_key, iter, and tree.
339
+ In addition, we verify that the model object is of type `ModelArtifact`.
340
+
341
+ :param model_endpoint: An object representing the model endpoint that will be enriched with the model uri.
342
+ :param model_obj: An object representing the model artifact.
343
+
344
+ :raise: `MLRunInvalidArgumentError` if the model object is not of type `ModelArtifact`.
345
+ """
346
+ mlrun.utils.helpers.verify_field_of_type(
347
+ field_name="model_endpoint.spec.model_uri",
348
+ field_value=model_obj,
349
+ expected_type=mlrun.artifacts.ModelArtifact,
350
+ )
351
+
352
+ # Update model_uri with a unique reference to handle future changes
353
+ model_artifact_uri = mlrun.utils.helpers.generate_artifact_uri(
354
+ project=model_endpoint.metadata.project,
355
+ key=model_obj.db_key,
356
+ iter=model_obj.iter,
357
+ tree=model_obj.tree,
358
+ )
359
+ model_endpoint.spec.model_uri = mlrun.datastore.get_store_uri(
360
+ kind=mlrun.utils.helpers.StorePrefix.Model, uri=model_artifact_uri
361
+ )
@@ -37,6 +37,7 @@ from mlrun.common.schemas.model_monitoring.constants import (
37
37
  ModelEndpointTarget,
38
38
  ProjectSecretKeys,
39
39
  )
40
+ from mlrun.model_monitoring.db import StoreBase, TSDBConnector
40
41
  from mlrun.utils import logger
41
42
 
42
43
 
@@ -48,14 +49,12 @@ class EventStreamProcessor:
48
49
  parquet_batching_max_events: int,
49
50
  parquet_batching_timeout_secs: int,
50
51
  parquet_target: str,
51
- sample_window: int = 10,
52
52
  aggregate_windows: typing.Optional[list[str]] = None,
53
- aggregate_period: str = "30s",
53
+ aggregate_period: str = "5m",
54
54
  model_monitoring_access_key: str = None,
55
55
  ):
56
56
  # General configurations, mainly used for the storey steps in the future serving graph
57
57
  self.project = project
58
- self.sample_window = sample_window
59
58
  self.aggregate_windows = aggregate_windows or ["5m", "1h"]
60
59
  self.aggregate_period = aggregate_period
61
60
 
@@ -133,7 +132,8 @@ class EventStreamProcessor:
133
132
  def apply_monitoring_serving_graph(
134
133
  self,
135
134
  fn: mlrun.runtimes.ServingRuntime,
136
- secret_provider: typing.Optional[typing.Callable[[str], str]] = None,
135
+ tsdb_connector: TSDBConnector,
136
+ endpoint_store: StoreBase,
137
137
  ) -> None:
138
138
  """
139
139
  Apply monitoring serving graph to a given serving function. The following serving graph includes about 4 main
@@ -161,8 +161,8 @@ class EventStreamProcessor:
161
161
  using CE, the parquet target path is based on the defined MLRun artifact path.
162
162
 
163
163
  :param fn: A serving function.
164
- :param secret_provider: An optional callable function that provides the connection string from the project
165
- secret.
164
+ :param tsdb_connector: Time series database connector.
165
+ :param endpoint_store: KV/SQL store used for endpoint data.
166
166
  """
167
167
 
168
168
  graph = typing.cast(
@@ -190,10 +190,6 @@ class EventStreamProcessor:
190
190
  _fn="(event.get('error') is not None)",
191
191
  )
192
192
 
193
- tsdb_connector = mlrun.model_monitoring.get_tsdb_connector(
194
- project=self.project, secret_provider=secret_provider
195
- )
196
-
197
193
  tsdb_connector.handle_model_error(
198
194
  graph,
199
195
  )
@@ -306,24 +302,9 @@ class EventStreamProcessor:
306
302
  table=self.kv_path,
307
303
  )
308
304
 
309
- store_object = mlrun.model_monitoring.get_store_object(
310
- project=self.project, secret_provider=secret_provider
311
- )
312
- if store_object.type == ModelEndpointTarget.V3IO_NOSQL:
305
+ if endpoint_store.type == ModelEndpointTarget.V3IO_NOSQL:
313
306
  apply_infer_schema()
314
307
 
315
- # Emits the event in window size of events based on sample_window size (10 by default)
316
- def apply_storey_sample_window():
317
- graph.add_step(
318
- "storey.steps.SampleWindow",
319
- name="sample",
320
- after="Rename",
321
- window_size=self.sample_window,
322
- key=EventFieldType.ENDPOINT_ID,
323
- )
324
-
325
- apply_storey_sample_window()
326
-
327
308
  tsdb_connector.apply_monitoring_stream_steps(graph=graph)
328
309
 
329
310
  # Parquet branch
@@ -353,6 +334,7 @@ class EventStreamProcessor:
353
334
  index_cols=[EventFieldType.ENDPOINT_ID],
354
335
  key_bucketing_number=0,
355
336
  time_partitioning_granularity="hour",
337
+ time_field=EventFieldType.TIMESTAMP,
356
338
  partition_cols=["$key", "$year", "$month", "$day", "$hour"],
357
339
  )
358
340
 
@@ -34,7 +34,7 @@ class _DefaultPackagerMeta(ABCMeta):
34
34
  dynamically generated docstring that will include a summary of the packager.
35
35
  """
36
36
 
37
- def __new__(mcls, name: str, bases: tuple, namespace: dict, **kwargs):
37
+ def __new__(cls, name: str, bases: tuple, namespace: dict, **kwargs):
38
38
  """
39
39
  Create a new DefaultPackager metaclass that saves the original packager docstring to another attribute named
40
40
  `_packager_doc`.
@@ -48,7 +48,7 @@ class _DefaultPackagerMeta(ABCMeta):
48
48
  namespace["_packager_doc"] = namespace.get("__doc__", "")
49
49
 
50
50
  # Continue creating the metaclass:
51
- return super().__new__(mcls, name, bases, namespace, **kwargs)
51
+ return super().__new__(cls, name, bases, namespace, **kwargs)
52
52
 
53
53
  @property
54
54
  def __doc__(cls: type["DefaultPackager"]) -> str:
mlrun/projects/project.py CHANGED
@@ -1557,15 +1557,15 @@ class MlrunProject(ModelObj):
1557
1557
  self,
1558
1558
  item,
1559
1559
  body=None,
1560
- tag="",
1561
- local_path="",
1562
- artifact_path=None,
1563
- format=None,
1564
- upload=None,
1565
- labels=None,
1566
- target_path=None,
1560
+ tag: str = "",
1561
+ local_path: str = "",
1562
+ artifact_path: Optional[str] = None,
1563
+ format: Optional[str] = None,
1564
+ upload: Optional[bool] = None,
1565
+ labels: Optional[dict[str, str]] = None,
1566
+ target_path: Optional[str] = None,
1567
1567
  **kwargs,
1568
- ):
1568
+ ) -> Artifact:
1569
1569
  """Log an output artifact and optionally upload it to datastore
1570
1570
 
1571
1571
  If the artifact already exists with the same key and tag, it will be overwritten.
@@ -1664,7 +1664,7 @@ class MlrunProject(ModelObj):
1664
1664
  stats=None,
1665
1665
  target_path="",
1666
1666
  extra_data=None,
1667
- label_column: str = None,
1667
+ label_column: Optional[str] = None,
1668
1668
  **kwargs,
1669
1669
  ) -> DatasetArtifact:
1670
1670
  """
@@ -1741,15 +1741,15 @@ class MlrunProject(ModelObj):
1741
1741
  artifact_path=None,
1742
1742
  upload=None,
1743
1743
  labels=None,
1744
- inputs: list[Feature] = None,
1745
- outputs: list[Feature] = None,
1746
- feature_vector: str = None,
1747
- feature_weights: list = None,
1744
+ inputs: Optional[list[Feature]] = None,
1745
+ outputs: Optional[list[Feature]] = None,
1746
+ feature_vector: Optional[str] = None,
1747
+ feature_weights: Optional[list] = None,
1748
1748
  training_set=None,
1749
1749
  label_column=None,
1750
1750
  extra_data=None,
1751
1751
  **kwargs,
1752
- ):
1752
+ ) -> ModelArtifact:
1753
1753
  """Log a model artifact and optionally upload it to datastore
1754
1754
 
1755
1755
  If the model already exists with the same key and tag, it will be overwritten.
@@ -3040,8 +3040,9 @@ class MlrunProject(ModelObj):
3040
3040
  "Remote repo is not defined, use .create_remote() + push()"
3041
3041
  )
3042
3042
 
3043
- if engine not in ["remote"]:
3044
- # for remote runs we don't require the functions to be synced as they can be loaded dynamically during run
3043
+ if engine not in ["remote"] and not schedule:
3044
+ # For remote/scheduled runs we don't require the functions to be synced as they can be loaded dynamically
3045
+ # during run
3045
3046
  self.sync_functions(always=sync)
3046
3047
  if not self.spec._function_objects:
3047
3048
  raise ValueError(
mlrun/runtimes/funcdoc.py CHANGED
@@ -247,7 +247,7 @@ class ASTVisitor(ast.NodeVisitor):
247
247
  self.exprs.append(node)
248
248
  super().generic_visit(node)
249
249
 
250
- def visit_FunctionDef(self, node):
250
+ def visit_FunctionDef(self, node): # noqa: N802
251
251
  self.funcs.append(node)
252
252
  self.generic_visit(node)
253
253
 
@@ -578,6 +578,15 @@ class APIGateway(ModelObj):
578
578
  "true"
579
579
  )
580
580
 
581
+ def with_gateway_timeout(self, gateway_timeout: int):
582
+ """
583
+ Set gateway proxy connect/read/send timeout annotations
584
+ :param gateway_timeout: The timeout in seconds
585
+ """
586
+ mlrun.runtimes.utils.enrich_gateway_timeout_annotations(
587
+ self.metadata.annotations, gateway_timeout
588
+ )
589
+
581
590
  @classmethod
582
591
  def from_scheme(cls, api_gateway: schemas.APIGateway):
583
592
  project = api_gateway.metadata.labels.get(