Files
2026-07-13 13:22:34 +08:00

218 lines
8.0 KiB
Python

import pytest
import mlflow
from mlflow.entities import Metric
from mlflow.evaluation import Assessment, Evaluation, log_evaluations
from mlflow.evaluation.assessment import AssessmentSource, AssessmentSourceType
from mlflow.evaluation.evaluation_tag import EvaluationTag
from tests.evaluate.logging.utils import get_evaluation
@pytest.fixture
def end_run_at_test_end():
yield
mlflow.end_run()
def test_log_evaluations_with_minimal_params_succeeds():
inputs1 = {"feature1": 1.0, "feature2": 2.0}
outputs1 = {"prediction": 0.5}
inputs2 = {"feature3": 3.0, "feature4": 4.0}
outputs2 = {"prediction": 0.8}
with mlflow.start_run():
# Create evaluation objects
evaluation1 = Evaluation(inputs=inputs1, outputs=outputs1)
evaluation2 = Evaluation(inputs=inputs2, outputs=outputs2)
# Log the evaluations
logged_evaluations = log_evaluations(evaluations=[evaluation1, evaluation2])
assert len(logged_evaluations) == 2
for logged_evaluation, expected_evaluation in zip(
logged_evaluations, [evaluation1, evaluation2]
):
assert logged_evaluation.inputs == expected_evaluation.inputs
assert logged_evaluation.outputs == expected_evaluation.outputs
retrieved_evaluation = get_evaluation(
evaluation_id=logged_evaluation.evaluation_id,
run_id=mlflow.active_run().info.run_id,
)
assert retrieved_evaluation is not None
assert retrieved_evaluation.inputs == logged_evaluation.inputs
assert retrieved_evaluation.outputs == logged_evaluation.outputs
def test_log_evaluations_with_all_params():
evaluations_data = [
(
{"feature1": 1.0, "feature2": 2.0},
{"prediction": 0.5},
{"actual": 1.0},
[
{
"name": "assessment1",
"value": 1.0,
"source": {
"source_type": "HUMAN",
"source_id": "user_1",
},
},
{
"name": "assessment2",
"value": 0.84,
"source": {
"source_type": "HUMAN",
"source_id": "user_1",
},
},
],
[
Metric(key="metric1", value=1.4, timestamp=1717047609503, step=0),
Metric(key="metric2", value=1.2, timestamp=1717047609504, step=0),
],
{"tag1": "value1", "tag2": "value2"},
),
(
{"feature1": "text1", "feature2": "text2"},
{"prediction": "output_text"},
{"actual": "expected_text"},
[
Assessment(
name="accuracy",
value=0.8,
source=AssessmentSource(
source_type=AssessmentSourceType.HUMAN,
source_id="user-1",
),
)
],
{"metric1": 0.8, "metric2": 0.84},
{"tag3": "value3", "tag4": "value4"},
),
]
inputs_id = "unique-inputs-id"
request_id = "unique-request-id"
with mlflow.start_run() as run:
run_id = run.info.run_id
evaluations = []
for inputs, outputs, targets, assessments, metrics, tags in evaluations_data:
if isinstance(assessments[0], dict):
assessments = [Assessment.from_dictionary(assessment) for assessment in assessments]
if isinstance(metrics, dict):
metrics = [
Metric(key=key, value=value, timestamp=0, step=0)
for key, value in metrics.items()
]
evaluation = Evaluation(
inputs=inputs,
outputs=outputs,
inputs_id=inputs_id,
request_id=request_id,
targets=targets,
assessments=assessments,
metrics=metrics,
tags=tags,
)
evaluations.append(evaluation)
# Log the evaluations
logged_evaluations = log_evaluations(evaluations=evaluations, run_id=run_id)
for logged_evaluation, (inputs, outputs, targets, assessments, metrics, tags) in zip(
logged_evaluations, evaluations_data
):
# Assert the fields of the logged evaluation
assert logged_evaluation.inputs == inputs
assert logged_evaluation.outputs == outputs
assert logged_evaluation.inputs_id == inputs_id
assert logged_evaluation.request_id == request_id
assert logged_evaluation.targets == targets
logged_metrics = (
{metric.key: metric.value for metric in logged_evaluation.metrics}
if isinstance(metrics, list) and isinstance(metrics[0], Metric)
else metrics
)
assert {
metric.key: metric.value for metric in logged_evaluation.metrics
} == logged_metrics
logged_tags = (
{tag.key: tag.value for tag in logged_evaluation.tags}
if isinstance(tags, list) and isinstance(tags[0], EvaluationTag)
else tags
)
assert {tag.key: tag.value for tag in logged_evaluation.tags} == logged_tags
assessment_entities = [
Assessment.from_dictionary(assessment)._to_entity(
evaluation_id=logged_evaluation.evaluation_id
)
if isinstance(assessment, dict)
else assessment._to_entity(evaluation_id=logged_evaluation.evaluation_id)
for assessment in assessments
]
for logged_assessment, assessment_entity in zip(
logged_evaluation.assessments, assessment_entities
):
assert logged_assessment.name == assessment_entity.name
assert logged_assessment.boolean_value == assessment_entity.boolean_value
assert logged_assessment.numeric_value == assessment_entity.numeric_value
assert logged_assessment.string_value == assessment_entity.string_value
assert logged_assessment.metadata == assessment_entity.metadata
assert logged_assessment.source == assessment_entity.source
retrieved_evaluation = get_evaluation(
evaluation_id=logged_evaluation.evaluation_id, run_id=run_id
)
assert logged_evaluation == retrieved_evaluation
def test_log_evaluations_starts_run_if_not_started(end_run_at_test_end):
inputs = {"feature1": 1.0, "feature2": {"nested_feature": 2.0}}
outputs = {"prediction": 0.5}
# Ensure there is no active run
if mlflow.active_run() is not None:
mlflow.end_run()
# Log evaluation without explicitly starting a run
logged_evaluation = log_evaluations(evaluations=[Evaluation(inputs=inputs, outputs=outputs)])[0]
# Verify that a run has been started
active_run = mlflow.active_run()
assert active_run is not None, "Expected a run to be started automatically."
# Retrieve the evaluation using the run ID
retrieved_evaluation = get_evaluation(
evaluation_id=logged_evaluation.evaluation_id, run_id=active_run.info.run_id
)
assert retrieved_evaluation == logged_evaluation
def test_evaluation_module_exposes_relevant_apis_for_logging():
import mlflow.evaluation
assert hasattr(mlflow.evaluation, "log_evaluations")
assert hasattr(mlflow.evaluation, "Evaluation")
assert hasattr(mlflow.evaluation, "Assessment")
assert hasattr(mlflow.evaluation, "AssessmentSource")
assert hasattr(mlflow.evaluation, "AssessmentSourceType")
def test_log_evaluations_works_properly_with_empty_evaluations_list():
with mlflow.start_run():
log_evaluations(evaluations=[])
artifacts = mlflow.MlflowClient().list_artifacts(mlflow.active_run().info.run_id)
assert len(artifacts) == 0