Source code for mlrun.model_monitoring.applications.evidently_base
# Copyright 2023 Iguazio
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
import warnings
from abc import ABC
import pandas as pd
import semver
import mlrun.model_monitoring.applications.base as mm_base
import mlrun.model_monitoring.applications.context as mm_context
from mlrun.errors import MLRunIncompatibleVersionError
SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.4.39")
def _check_evidently_version(*, cur: semver.Version, ref: semver.Version) -> None:
if ref.is_compatible(cur) or (
cur.major == ref.major == 0 and cur.minor == ref.minor and cur.patch > ref.patch
):
return
if cur.major == ref.major == 0 and cur.minor > ref.minor:
warnings.warn(
f"Evidently version {cur} is not compatible with the tested "
f"version {ref}, use at your own risk."
)
else:
raise MLRunIncompatibleVersionError(
f"Evidently version {cur} is not supported, please change to "
f"{ref} (or another compatible version)."
)
_HAS_EVIDENTLY = False
try:
import evidently # noqa: F401
_check_evidently_version(
cur=semver.Version.parse(evidently.__version__),
ref=SUPPORTED_EVIDENTLY_VERSION,
)
_HAS_EVIDENTLY = True
except ModuleNotFoundError:
pass
if _HAS_EVIDENTLY:
from evidently.suite.base_suite import Display
from evidently.ui.type_aliases import STR_UUID
from evidently.ui.workspace import Workspace
from evidently.utils.dashboard import TemplateParams, file_html_template
[docs]class EvidentlyModelMonitoringApplicationBase(
mm_base.ModelMonitoringApplicationBase, ABC
):
def __init__(
self, evidently_workspace_path: str, evidently_project_id: "STR_UUID"
) -> None:
"""
A class for integrating Evidently for mlrun model monitoring within a monitoring application.
Note: evidently is not installed by default in the mlrun/mlrun image.
It must be installed separately to use this class.
:param evidently_workspace_path: (str) The path to the Evidently workspace.
:param evidently_project_id: (str) The ID of the Evidently project.
"""
# TODO : more then one project (mep -> project)
if not _HAS_EVIDENTLY:
raise ModuleNotFoundError("Evidently is not installed - the app cannot run")
self.evidently_workspace = Workspace.create(evidently_workspace_path)
self.evidently_project_id = evidently_project_id
self.evidently_project = self.evidently_workspace.get_project(
evidently_project_id
)
[docs] @staticmethod
def log_evidently_object(
monitoring_context: mm_context.MonitoringApplicationContext,
evidently_object: "Display",
artifact_name: str,
) -> None:
"""
Logs an Evidently report or suite as an artifact.
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
:param evidently_object: (Display) The Evidently display to log, e.g. a report or a test suite object.
:param artifact_name: (str) The name for the logged artifact.
"""
evidently_object_html = evidently_object.get_html()
monitoring_context.log_artifact(
artifact_name, body=evidently_object_html.encode("utf-8"), format="html"
)
[docs] def log_project_dashboard(
self,
monitoring_context: mm_context.MonitoringApplicationContext,
timestamp_start: pd.Timestamp,
timestamp_end: pd.Timestamp,
artifact_name: str = "dashboard",
) -> None:
"""
Logs an Evidently project dashboard.
:param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
:param timestamp_start: (pd.Timestamp) The start timestamp for the dashboard data.
:param timestamp_end: (pd.Timestamp) The end timestamp for the dashboard data.
:param artifact_name: (str) The name for the logged artifact.
"""
dashboard_info = self.evidently_project.build_dashboard_info(
timestamp_start, timestamp_end
)
template_params = TemplateParams(
dashboard_id="pd_" + str(uuid.uuid4()).replace("-", ""),
dashboard_info=dashboard_info,
additional_graphs={},
)
dashboard_html = file_html_template(params=template_params)
monitoring_context.log_artifact(
artifact_name, body=dashboard_html.encode("utf-8"), format="html"
)