mlrun 1.8.0rc29__py3-none-any.whl → 1.8.0rc31__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 (50) hide show
  1. mlrun/__init__.py +2 -34
  2. mlrun/api/schemas/__init__.py +1 -6
  3. mlrun/artifacts/document.py +3 -3
  4. mlrun/artifacts/manager.py +1 -0
  5. mlrun/artifacts/model.py +3 -3
  6. mlrun/common/model_monitoring/helpers.py +16 -7
  7. mlrun/common/runtimes/constants.py +5 -0
  8. mlrun/common/schemas/__init__.py +0 -2
  9. mlrun/common/schemas/model_monitoring/__init__.py +0 -2
  10. mlrun/common/schemas/model_monitoring/constants.py +4 -7
  11. mlrun/common/schemas/model_monitoring/grafana.py +17 -11
  12. mlrun/config.py +9 -36
  13. mlrun/datastore/datastore_profile.py +1 -1
  14. mlrun/datastore/sources.py +14 -13
  15. mlrun/datastore/storeytargets.py +20 -3
  16. mlrun/db/httpdb.py +4 -30
  17. mlrun/k8s_utils.py +2 -5
  18. mlrun/launcher/base.py +16 -0
  19. mlrun/model_monitoring/api.py +1 -2
  20. mlrun/model_monitoring/applications/_application_steps.py +23 -37
  21. mlrun/model_monitoring/applications/base.py +55 -40
  22. mlrun/model_monitoring/applications/context.py +0 -3
  23. mlrun/model_monitoring/applications/results.py +16 -16
  24. mlrun/model_monitoring/controller.py +35 -31
  25. mlrun/model_monitoring/db/tsdb/__init__.py +9 -5
  26. mlrun/model_monitoring/db/tsdb/base.py +60 -39
  27. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +122 -53
  28. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +140 -14
  29. mlrun/model_monitoring/helpers.py +124 -16
  30. mlrun/model_monitoring/stream_processing.py +6 -21
  31. mlrun/projects/pipelines.py +11 -3
  32. mlrun/projects/project.py +104 -115
  33. mlrun/run.py +2 -2
  34. mlrun/runtimes/nuclio/function.py +4 -2
  35. mlrun/serving/routers.py +3 -4
  36. mlrun/serving/server.py +10 -8
  37. mlrun/serving/states.py +12 -2
  38. mlrun/serving/v2_serving.py +25 -20
  39. mlrun/utils/async_http.py +32 -19
  40. mlrun/utils/helpers.py +5 -2
  41. mlrun/utils/logger.py +14 -10
  42. mlrun/utils/notifications/notification_pusher.py +25 -0
  43. mlrun/utils/regex.py +1 -0
  44. mlrun/utils/version/version.json +2 -2
  45. {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/METADATA +4 -4
  46. {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/RECORD +50 -50
  47. {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/LICENSE +0 -0
  48. {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/WHEEL +0 -0
  49. {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/entry_points.txt +0 -0
  50. {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py CHANGED
@@ -29,7 +29,6 @@ import zipfile
29
29
  from copy import deepcopy
30
30
  from os import environ, makedirs, path
31
31
  from typing import Callable, Optional, Union, cast
32
- from urllib.parse import urlparse
33
32
 
34
33
  import deprecated
35
34
  import dotenv
@@ -71,6 +70,7 @@ from mlrun.datastore.datastore_profile import (
71
70
  from mlrun.datastore.vectorstore import VectorStoreCollection
72
71
  from mlrun.model_monitoring.helpers import (
73
72
  filter_results_by_regex,
73
+ get_alert_name_from_result_fqn,
74
74
  get_result_instance_fqn,
75
75
  )
76
76
  from mlrun.runtimes.nuclio.function import RemoteRuntime
@@ -1254,8 +1254,14 @@ class MlrunProject(ModelObj):
1254
1254
  mlrun.utils.helpers.validate_builder_source(source, pull_at_runtime, workdir)
1255
1255
 
1256
1256
  self.spec.load_source_on_run = pull_at_runtime
1257
+
1258
+ source_has_changed = source != self.spec.source
1257
1259
  self.spec.source = source or self.spec.source
1258
1260
 
1261
+ # new source should not relay on old workdir
1262
+ if source_has_changed:
1263
+ self.spec.workdir = workdir
1264
+
1259
1265
  if self.spec.source.startswith("git://"):
1260
1266
  source, reference, branch = resolve_git_reference_from_source(source)
1261
1267
  if not branch and not reference:
@@ -1264,7 +1270,6 @@ class MlrunProject(ModelObj):
1264
1270
  "'git://<url>/org/repo.git#<branch-name or refs/heads/..>'"
1265
1271
  )
1266
1272
 
1267
- self.spec.workdir = workdir or self.spec.workdir
1268
1273
  try:
1269
1274
  # reset function objects (to recalculate build attributes)
1270
1275
  self.sync_functions()
@@ -2137,7 +2142,8 @@ class MlrunProject(ModelObj):
2137
2142
  reset_policy: mlrun.common.schemas.alert.ResetPolicy = mlrun.common.schemas.alert.ResetPolicy.AUTO,
2138
2143
  ) -> list[mlrun.alerts.alert.AlertConfig]:
2139
2144
  """
2140
- :param name: AlertConfig name.
2145
+ :param name: The name of the AlertConfig template. It will be combined with mep_id, app-name
2146
+ and result name to generate a unique name.
2141
2147
  :param summary: Summary of the alert, will be sent in the generated notifications
2142
2148
  :param endpoints: The endpoints from which metrics will be retrieved to configure the alerts.
2143
2149
  This `ModelEndpointList` object obtained via the `list_model_endpoints`
@@ -2198,10 +2204,11 @@ class MlrunProject(ModelObj):
2198
2204
  )
2199
2205
  alert_result_names = list(set(specific_result_names + matching_results))
2200
2206
  for result_fqn in alert_result_names:
2207
+ result_fqn_name = get_alert_name_from_result_fqn(result_fqn)
2201
2208
  alerts.append(
2202
2209
  mlrun.alerts.alert.AlertConfig(
2203
2210
  project=self.name,
2204
- name=name,
2211
+ name=f"{name}--{result_fqn_name}",
2205
2212
  summary=summary,
2206
2213
  severity=severity,
2207
2214
  entities=alert_constants.EventEntities(
@@ -2217,6 +2224,12 @@ class MlrunProject(ModelObj):
2217
2224
  reset_policy=reset_policy,
2218
2225
  )
2219
2226
  )
2227
+ if not alerts:
2228
+ warnings.warn(
2229
+ "No alert config has been created. "
2230
+ "Try specifying a result name explicitly or verifying that results are available"
2231
+ )
2232
+
2220
2233
  return alerts
2221
2234
 
2222
2235
  def set_model_monitoring_function(
@@ -3456,12 +3469,13 @@ class MlrunProject(ModelObj):
3456
3469
 
3457
3470
  arguments = arguments or {}
3458
3471
  need_repo = self.spec._need_repo()
3459
- if self.spec.repo and self.spec.repo.is_dirty():
3460
- msg = "You seem to have uncommitted git changes, use .push()"
3461
- if dirty or not need_repo:
3462
- logger.warning("WARNING!, " + msg)
3463
- else:
3464
- raise ProjectError(msg + " or dirty=True")
3472
+ if not dirty:
3473
+ if self.spec.repo and self.spec.repo.is_dirty():
3474
+ msg = "You seem to have uncommitted git changes, use .push()"
3475
+ if not need_repo:
3476
+ logger.warning("WARNING!, " + msg)
3477
+ else:
3478
+ raise ProjectError(msg + " or dirty=True")
3465
3479
 
3466
3480
  if need_repo and self.spec.repo and not self.spec.source:
3467
3481
  raise ProjectError(
@@ -3659,50 +3673,77 @@ class MlrunProject(ModelObj):
3659
3673
 
3660
3674
  def set_model_monitoring_credentials(
3661
3675
  self,
3662
- access_key: Optional[str] = None,
3663
- stream_path: Optional[str] = None, # Deprecated
3664
- tsdb_connection: Optional[str] = None, # Deprecated
3665
- replace_creds: bool = False,
3666
3676
  *,
3667
- stream_profile_name: Optional[str] = None,
3668
- tsdb_profile_name: Optional[str] = None,
3669
- ):
3677
+ tsdb_profile_name: str,
3678
+ stream_profile_name: str,
3679
+ replace_creds: bool = False,
3680
+ ) -> None:
3670
3681
  """
3671
- Set the credentials that will be used by the project's model monitoring
3672
- infrastructure functions. Important to note that you have to set the credentials before deploying any
3673
- model monitoring or serving function.
3674
-
3675
- :param access_key: Model monitoring access key for managing user permissions.
3676
-
3677
- * None - will be set from the system configuration.
3678
- * v3io - for v3io endpoint store, pass `v3io` and the system will generate the
3679
- exact path.
3680
- :param stream_path: (Deprecated) This argument is deprecated. Use ``stream_profile_name`` instead.
3681
- Path to the model monitoring stream. By default, None. Options:
3682
-
3683
- * ``"v3io"`` - for v3io stream, pass ``"v3io"`` and the system will generate
3684
- the exact path.
3685
- * Kafka - for Kafka stream, provide the full connection string without acustom
3686
- topic, for example ``"kafka://<some_kafka_broker>:<port>"``.
3687
- :param tsdb_connection: (Deprecated) Connection string to the time series database. By default, None.
3688
- Options:
3689
-
3690
- * v3io - for v3io stream, pass ``"v3io"`` and the system will generate the
3691
- exact path.
3692
- * TDEngine - for TDEngine tsdb, provide the full websocket connection URL,
3693
- for example ``"taosws://<username>:<password>@<host>:<port>"``.
3694
- :param replace_creds: If True, will override the existing credentials.
3695
- Please keep in mind that if you already enabled model monitoring on
3696
- your project this action can cause data loose and will require redeploying
3697
- all model monitoring functions & model monitoring infra
3698
- & tracked model server.
3699
- :param stream_profile_name: The datastore profile name of the stream to be used in model monitoring.
3700
- The supported profiles are:
3682
+ Set the credentials that will be used by the project's model monitoring infrastructure functions.
3683
+ Please note that you have to set the credentials before deploying any model monitoring application
3684
+ or a tracked serving function.
3701
3685
 
3702
- * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileV3io`
3703
- * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource`
3686
+ For example, the full flow for enabling model monitoring infrastructure with **TDEngine** and **Kafka**, is:
3687
+
3688
+ .. code-block:: python
3689
+
3690
+ import mlrun
3691
+ from mlrun.datastore.datastore_profile import (
3692
+ DatastoreProfileKafkaSource,
3693
+ TDEngineDatastoreProfile,
3694
+ )
3695
+
3696
+ project = mlrun.get_or_create_project("mm-infra-setup")
3697
+
3698
+ # Create and register TSDB profile
3699
+ tsdb_profile = TDEngineDatastoreProfile(
3700
+ name="my-tdengine",
3701
+ host="<tdengine-server-ip-address>",
3702
+ port=6041,
3703
+ user="username",
3704
+ password="<tdengine-password>",
3705
+ )
3706
+ project.register_datastore_profile(tsdb_profile)
3707
+
3708
+ # Create and register stream profile
3709
+ stream_profile = DatastoreProfileKafkaSource(
3710
+ name="my-kafka",
3711
+ brokers=["<kafka-broker-ip-address>:9094"],
3712
+ topics=[], # Keep the topics list empty
3713
+ ## SASL is supported
3714
+ # sasl_user="user1",
3715
+ # sasl_pass="<kafka-sasl-password>",
3716
+ )
3717
+ project.register_datastore_profile(stream_profile)
3718
+
3719
+ # Set model monitoring credentials and enable the infrastructure
3720
+ project.set_model_monitoring_credentials(
3721
+ tsdb_profile_name=tsdb_profile.name,
3722
+ stream_profile_name=stream_profile.name,
3723
+ )
3724
+ project.enable_model_monitoring()
3725
+
3726
+ Note that you will need to change the profiles if you want to use **V3IO** TSDB and stream:
3727
+
3728
+ .. code-block:: python
3729
+
3730
+ from mlrun.datastore.datastore_profile import DatastoreProfileV3io
3731
+
3732
+ # Create and register TSDB profile
3733
+ tsdb_profile = DatastoreProfileV3io(
3734
+ name="my-v3io-tsdb",
3735
+ )
3736
+ project.register_datastore_profile(tsdb_profile)
3737
+
3738
+ # Create and register stream profile
3739
+ stream_profile = DatastoreProfileV3io(
3740
+ name="my-v3io-stream",
3741
+ v3io_access_key=mlrun.mlconf.get_v3io_access_key(),
3742
+ )
3743
+ project.register_datastore_profile(stream_profile)
3744
+
3745
+ In the V3IO datastore, you must provide an explicit access key to the stream, but not to the TSDB.
3704
3746
 
3705
- You need to register one of them, and pass the profile's name.
3706
3747
  :param tsdb_profile_name: The datastore profile name of the time-series database to be used in model
3707
3748
  monitoring. The supported profiles are:
3708
3749
 
@@ -3710,76 +3751,24 @@ class MlrunProject(ModelObj):
3710
3751
  * :py:class:`~mlrun.datastore.datastore_profile.TDEngineDatastoreProfile`
3711
3752
 
3712
3753
  You need to register one of them, and pass the profile's name.
3713
- """
3714
- db = mlrun.db.get_run_db(secrets=self._secrets)
3715
-
3716
- if tsdb_connection:
3717
- warnings.warn(
3718
- "The `tsdb_connection` argument is deprecated and will be removed in MLRun version 1.8.0. "
3719
- "Use `tsdb_profile_name` instead.",
3720
- FutureWarning,
3721
- )
3722
- if tsdb_profile_name:
3723
- raise mlrun.errors.MLRunValueError(
3724
- "If you set `tsdb_profile_name`, you must not pass `tsdb_connection`."
3725
- )
3726
- if tsdb_connection == "v3io":
3727
- tsdb_profile = mlrun.datastore.datastore_profile.DatastoreProfileV3io(
3728
- name=mm_constants.DefaultProfileName.TSDB
3729
- )
3730
- else:
3731
- parsed_url = urlparse(tsdb_connection)
3732
- if parsed_url.scheme != "taosws":
3733
- raise mlrun.errors.MLRunValueError(
3734
- f"Unsupported `tsdb_connection`: '{tsdb_connection}'."
3735
- )
3736
- tsdb_profile = (
3737
- mlrun.datastore.datastore_profile.TDEngineDatastoreProfile(
3738
- name=mm_constants.DefaultProfileName.TSDB,
3739
- user=parsed_url.username,
3740
- password=parsed_url.password,
3741
- host=parsed_url.hostname,
3742
- port=parsed_url.port,
3743
- )
3744
- )
3754
+ :param stream_profile_name: The datastore profile name of the stream to be used in model monitoring.
3755
+ The supported profiles are:
3745
3756
 
3746
- self.register_datastore_profile(tsdb_profile)
3747
- tsdb_profile_name = tsdb_profile.name
3757
+ * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileV3io`
3758
+ * :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource`
3748
3759
 
3749
- if stream_path:
3750
- warnings.warn(
3751
- "The `stream_path` argument is deprecated and will be removed in MLRun version 1.8.0. "
3752
- "Use `stream_profile_name` instead.",
3753
- FutureWarning,
3754
- )
3755
- if stream_profile_name:
3756
- raise mlrun.errors.MLRunValueError(
3757
- "If you set `stream_profile_name`, you must not pass `stream_path`."
3758
- )
3759
- if stream_path == "v3io":
3760
- stream_profile = mlrun.datastore.datastore_profile.DatastoreProfileV3io(
3761
- name=mm_constants.DefaultProfileName.STREAM
3762
- )
3763
- else:
3764
- parsed_stream = urlparse(stream_path)
3765
- if parsed_stream.scheme != "kafka":
3766
- raise mlrun.errors.MLRunValueError(
3767
- f"Unsupported `stream_path`: '{stream_path}'."
3768
- )
3769
- stream_profile = (
3770
- mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource(
3771
- name=mm_constants.DefaultProfileName.STREAM,
3772
- brokers=[parsed_stream.netloc],
3773
- topics=[],
3774
- )
3775
- )
3776
- self.register_datastore_profile(stream_profile)
3777
- stream_profile_name = stream_profile.name
3760
+ You need to register one of them, and pass the profile's name.
3761
+ :param replace_creds: If ``True`` - override the existing credentials.
3762
+ Please keep in mind that if you have already enabled model monitoring
3763
+ on your project, replacing the credentials can cause data loss, and will
3764
+ require redeploying all the model monitoring functions, model monitoring
3765
+ infrastructure, and tracked model servers.
3766
+ """
3767
+ db = mlrun.db.get_run_db(secrets=self._secrets)
3778
3768
 
3779
3769
  db.set_model_monitoring_credentials(
3780
3770
  project=self.name,
3781
3771
  credentials={
3782
- "access_key": access_key,
3783
3772
  "tsdb_profile_name": tsdb_profile_name,
3784
3773
  "stream_profile_name": stream_profile_name,
3785
3774
  },
@@ -4120,7 +4109,7 @@ class MlrunProject(ModelObj):
4120
4109
 
4121
4110
  :param image: target image name/path. If not specified the project's existing `default_image` name will be
4122
4111
  used. If not set, the `mlconf.default_project_image_name` value will be used
4123
- :param set_as_default: set `image` to be the project's default image (default False)
4112
+ :param set_as_default: set `image` to be the project's default image (default True)
4124
4113
  :param with_mlrun: add the current mlrun package to the container build
4125
4114
  :param base_image: base image name/path (commands and source code will be added to it) defaults to
4126
4115
  mlrun.mlconf.default_base_image
mlrun/run.py CHANGED
@@ -911,7 +911,7 @@ def _run_pipeline(
911
911
 
912
912
  def retry_pipeline(
913
913
  run_id: str,
914
- project: Optional[str] = None,
914
+ project: str,
915
915
  namespace: Optional[str] = None,
916
916
  ) -> str:
917
917
  """Retry a pipeline run.
@@ -920,7 +920,7 @@ def retry_pipeline(
920
920
  retryable state, a new run is created as a clone of the original run.
921
921
 
922
922
  :param run_id: ID of the pipeline run to retry.
923
- :param project: Optional; name of the project associated with the pipeline run.
923
+ :param project: name of the project associated with the pipeline run.
924
924
  :param namespace: Optional; Kubernetes namespace to use if not the default.
925
925
 
926
926
  :returns: ID of the retried pipeline run or the ID of a cloned run if the original run is not retryable.
@@ -369,8 +369,9 @@ class RemoteRuntime(KubeResource):
369
369
  )
370
370
  """
371
371
  self.spec.build.source = source
372
- # update handler in function_handler
373
- self.spec.function_handler = handler
372
+ # update handler in function_handler if needed
373
+ if handler:
374
+ self.spec.function_handler = handler
374
375
  if workdir:
375
376
  self.spec.workdir = workdir
376
377
  if runtime:
@@ -1016,6 +1017,7 @@ class RemoteRuntime(KubeResource):
1016
1017
  ):
1017
1018
  """
1018
1019
  Add a sidecar container to the function pod
1020
+
1019
1021
  :param name: Sidecar container name.
1020
1022
  :param image: Sidecar container image.
1021
1023
  :param ports: Sidecar container ports to expose. Can be a single port or a list of ports.
mlrun/serving/routers.py CHANGED
@@ -596,7 +596,6 @@ class VotingEnsemble(ParallelRun):
596
596
  self.vote_type = vote_type
597
597
  self.vote_flag = True if self.vote_type is not None else False
598
598
  self.weights = weights
599
- self.version = kwargs.get("version", "v1")
600
599
  self.log_router = True
601
600
  self.prediction_col_name = prediction_col_name or "prediction"
602
601
  self.format_response_with_col_name_flag = format_response_with_col_name_flag
@@ -927,14 +926,14 @@ class VotingEnsemble(ParallelRun):
927
926
  "model_name": self.name,
928
927
  "outputs": votes,
929
928
  }
930
- if self.version:
931
- response_body["model_version"] = self.version
929
+ if self.model_endpoint_uid:
930
+ response_body["model_endpoint_uid"] = self.model_endpoint_uid
932
931
  response.body = response_body
933
932
  elif name == self.name and event.method == "GET" and not subpath:
934
933
  response = copy.copy(event)
935
934
  response_body = {
936
935
  "name": self.name,
937
- "version": self.version or "",
936
+ "model_endpoint_uid": self.model_endpoint_uid or "",
938
937
  "inputs": [],
939
938
  "outputs": [],
940
939
  }
mlrun/serving/server.py CHANGED
@@ -65,7 +65,7 @@ class _StreamContext:
65
65
  self.hostname = socket.gethostname()
66
66
  self.function_uri = function_uri
67
67
  self.output_stream = None
68
- self.stream_uri = None
68
+ stream_uri = None
69
69
  log_stream = parameters.get(FileTargetKind.LOG_STREAM, "")
70
70
 
71
71
  if (enabled or log_stream) and function_uri:
@@ -78,17 +78,19 @@ class _StreamContext:
78
78
 
79
79
  if log_stream == DUMMY_STREAM:
80
80
  # Dummy stream used for testing, see tests/serving/test_serving.py
81
- self.stream_uri = DUMMY_STREAM
81
+ stream_uri = DUMMY_STREAM
82
82
  elif not stream_args.get("mock"): # if not a mock: `context.is_mock = True`
83
- self.stream_uri = mlrun.model_monitoring.get_stream_path(
84
- project=project
85
- )
83
+ stream_uri = mlrun.model_monitoring.get_stream_path(project=project)
86
84
 
87
85
  if log_stream:
88
86
  # Update the stream path to the log stream value
89
- self.stream_uri = log_stream.format(project=project)
90
-
91
- self.output_stream = get_stream_pusher(self.stream_uri, **stream_args)
87
+ stream_uri = log_stream.format(project=project)
88
+ self.output_stream = get_stream_pusher(stream_uri, **stream_args)
89
+ else:
90
+ # Get the output stream from the profile
91
+ self.output_stream = mlrun.model_monitoring.helpers.get_output_stream(
92
+ project=project, mock=stream_args.get("mock", False)
93
+ )
92
94
 
93
95
 
94
96
  class GraphServer(ModelObj):
mlrun/serving/states.py CHANGED
@@ -652,6 +652,14 @@ class TaskStep(BaseStep):
652
652
  if isinstance(self.endpoint_type, schemas.EndpointType)
653
653
  else self.endpoint_type
654
654
  )
655
+ self.model_endpoint_creation_strategy = (
656
+ self.model_endpoint_creation_strategy.value
657
+ if isinstance(
658
+ self.model_endpoint_creation_strategy,
659
+ schemas.ModelEndpointCreationStrategy,
660
+ )
661
+ else self.model_endpoint_creation_strategy
662
+ )
655
663
  return super().to_dict(fields, exclude, strip)
656
664
 
657
665
 
@@ -755,9 +763,11 @@ class RouterStep(TaskStep):
755
763
  self._routes: ObjectDict = None
756
764
  self.routes = routes
757
765
  self.endpoint_type = schemas.EndpointType.ROUTER
766
+ if isinstance(class_name, type):
767
+ class_name = class_name.__name__
758
768
  self.model_endpoint_creation_strategy = (
759
769
  schemas.ModelEndpointCreationStrategy.INPLACE
760
- if class_name and "serving.VotingEnsemble" in class_name
770
+ if class_name and "VotingEnsemble" in class_name
761
771
  else schemas.ModelEndpointCreationStrategy.SKIP
762
772
  )
763
773
 
@@ -802,8 +812,8 @@ class RouterStep(TaskStep):
802
812
  * **archive**:
803
813
  1. If model endpoints with the same name exist, preserve them.
804
814
  2. Create a new model endpoint with the same name and set it to `latest`.
805
-
806
815
  """
816
+
807
817
  if len(self.routes.keys()) >= MAX_MODELS_PER_ROUTER and key not in self.routes:
808
818
  raise mlrun.errors.MLRunModelLimitExceededError(
809
819
  f"Router cannot support more than {MAX_MODELS_PER_ROUTER} model endpoints. "
@@ -95,9 +95,6 @@ class V2ModelServer(StepToDict):
95
95
  :param kwargs: extra arguments (can be accessed using self.get_param(key))
96
96
  """
97
97
  self.name = name
98
- self.version = ""
99
- if name and ":" in name:
100
- self.version = name.split(":", 1)[-1]
101
98
  self.context = context
102
99
  self.ready = False
103
100
  self.error = ""
@@ -118,6 +115,7 @@ class V2ModelServer(StepToDict):
118
115
  self.shard_by_endpoint = shard_by_endpoint
119
116
  self._model_logger = None
120
117
  self.initialized = False
118
+ self.output_schema = []
121
119
 
122
120
  def _load_and_update_state(self):
123
121
  try:
@@ -178,17 +176,15 @@ class V2ModelServer(StepToDict):
178
176
  "Model endpoint creation task name not provided",
179
177
  )
180
178
  try:
181
- self.model_endpoint_uid = (
182
- mlrun.get_run_db()
183
- .get_model_endpoint(
184
- project=server.project,
185
- name=self.name,
186
- function_name=server.function_name,
187
- function_tag=server.function_tag or "latest",
188
- tsdb_metrics=False,
189
- )
190
- .metadata.uid
179
+ model_endpoint = mlrun.get_run_db().get_model_endpoint(
180
+ project=server.project,
181
+ name=self.name,
182
+ function_name=server.function_name,
183
+ function_tag=server.function_tag or "latest",
184
+ tsdb_metrics=False,
191
185
  )
186
+ self.model_endpoint_uid = model_endpoint.metadata.uid
187
+ self.output_schema = model_endpoint.spec.label_names
192
188
  except mlrun.errors.MLRunNotFoundError:
193
189
  logger.info(
194
190
  "Model endpoint not found for this step; monitoring for this model will not be performed",
@@ -325,8 +321,8 @@ class V2ModelServer(StepToDict):
325
321
  "outputs": outputs,
326
322
  "timestamp": start.isoformat(sep=" ", timespec="microseconds"),
327
323
  }
328
- if self.version:
329
- response["model_version"] = self.version
324
+ if self.model_endpoint_uid:
325
+ response["model_endpoint_uid"] = self.model_endpoint_uid
330
326
  elif op == "ready" and event.method == "GET":
331
327
  # get model health operation
332
328
  setattr(event, "terminated", True)
@@ -352,7 +348,7 @@ class V2ModelServer(StepToDict):
352
348
  setattr(event, "terminated", True)
353
349
  event_body = {
354
350
  "name": self.name.split(":")[0],
355
- "version": self.version or "",
351
+ "model_endpoint_uid": self.model_endpoint_uid or "",
356
352
  "inputs": [],
357
353
  "outputs": [],
358
354
  }
@@ -386,8 +382,8 @@ class V2ModelServer(StepToDict):
386
382
  "model_name": self.name,
387
383
  "outputs": outputs,
388
384
  }
389
- if self.version:
390
- response["model_version"] = self.version
385
+ if self.model_endpoint_uid:
386
+ response["model_endpoint_uid"] = self.model_endpoint_uid
391
387
 
392
388
  elif hasattr(self, "op_" + op):
393
389
  # custom operation (child methods starting with "op_")
@@ -510,7 +506,6 @@ class _ModelLogPusher:
510
506
  self.verbose = context.verbose
511
507
  self.hostname = context.stream.hostname
512
508
  self.function_uri = context.stream.function_uri
513
- self.stream_path = context.stream.stream_uri
514
509
  self.sampling_percentage = float(context.get_param("sampling_percentage", 100))
515
510
  self.output_stream = output_stream or context.stream.output_stream
516
511
  self._worker = context.worker_id
@@ -520,7 +515,6 @@ class _ModelLogPusher:
520
515
  "class": self.model.__class__.__name__,
521
516
  "worker": self._worker,
522
517
  "model": self.model.name,
523
- "version": self.model.version,
524
518
  "host": self.hostname,
525
519
  "function_uri": self.function_uri,
526
520
  "endpoint_id": self.model.model_endpoint_uid,
@@ -571,6 +565,17 @@ class _ModelLogPusher:
571
565
  resp["outputs"] = [
572
566
  resp["outputs"][i] for i in sampled_requests_indices
573
567
  ]
568
+ if self.model.output_schema and len(self.model.output_schema) != len(
569
+ resp["outputs"][0]
570
+ ):
571
+ logger.info(
572
+ "The number of outputs returned by the model does not match the number of outputs "
573
+ "specified in the model endpoint.",
574
+ model_endpoint=self.model.name,
575
+ model_endpoint_id=self.model.model_endpoint_uid,
576
+ output_len=len(resp["outputs"][0]),
577
+ schema_len=len(self.model.output_schema),
578
+ )
574
579
 
575
580
  data = self.base_data()
576
581
  data["request"] = request