feat: enhance E2E testing setup and model reporting

- Added a new fixture to manage runtime report artifacts in a writable temp directory during E2E tests, addressing permission issues in local CI/dev environments.
- Updated `conftest.py` to include a requirements.txt file in the model packaging path for training activities.
- Refactored existing fixtures to use `pytest.fixture` instead of `pytest_asyncio.fixture` for better compatibility.
- Enhanced the `Reports` class to include a target alias for report metrics, ensuring compatibility with Evidently's reporting requirements.
- Introduced new test scenarios to validate the handling of missing and whitespace-only `date_column` inputs in the training workflow.

These changes improve the robustness of the E2E testing framework and enhance the clarity of model reporting metrics.
This commit is contained in:
2026-05-05 10:59:51 -03:00
parent ba9eb3d7c7
commit d1f9394879
29 changed files with 255 additions and 182 deletions

View File

@@ -111,8 +111,14 @@ class Reports:
]
self.metrics.extend(metrics)
if run:
mapping = ColumnMapping()
mapping.target = self.target_name
report = Report(metrics=metrics, options=self.options)
report.run(reference_data=self.ref_data, current_data=self.cur_data)
report.run(
reference_data=self.ref_data,
current_data=self.cur_data,
column_mapping=mapping,
)
self.sections['data_quality'] = report.as_dict()
if self.base_path:
# Note: Relies on Evidently's save_html() to properly manage file I/O
@@ -128,8 +134,14 @@ class Reports:
"""
self.metrics.append(DataDriftPreset(columns=columns))
if run:
mapping = ColumnMapping()
mapping.target = self.target_name
report = Report(metrics=[DataDriftPreset(columns=columns)], options=self.options)
report.run(reference_data=self.ref_data, current_data=self.cur_data)
report.run(
reference_data=self.ref_data,
current_data=self.cur_data,
column_mapping=mapping,
)
self.sections['data_drift'] = report.as_dict()
if self.base_path:
# Note: Relies on Evidently's save_html() to properly manage file I/O

View File

@@ -565,6 +565,13 @@ class DataManagerRepository(SientiaMonitoring):
current_data = y_val_pred[['prediction']].join(data.val_data, how='inner')
current_data_float = current_data.astype(np.float64)
# Evidently's ConflictTargetMetric expects a literal `target` column name.
# Keep the original target column and provide this alias for report metrics.
target_col = data.params.target_variable
reference_data_float['target'] = reference_data_float[target_col]
current_data_float['target'] = current_data_float[target_col]
# Initialize report generator
base_path = self._get_reports_directory()
data.run_dir = self._create_run_directory(base_path, data.run_name)
@@ -581,7 +588,6 @@ class DataManagerRepository(SientiaMonitoring):
)
# Generate report sections
target_col = data.params.target_variable
feature_and_target_cols = data.params.variable_columns + [target_col]
report.add_data_quality_section(columns=feature_and_target_cols)
report.add_data_drift_section(columns=feature_and_target_cols)

View File

@@ -17,6 +17,7 @@ with workflow.unsafe.imports_passed_through():
from model_manager.workflows.train_model import no_retry_policy
TIMEOUT_CLEANUP_LOCAL = int(os.getenv('TIMEOUT_CLEANUP_LOCAL', '120'))
POD_ID = os.getenv('POD_ID')
@workflow.defn(name='cleanup_files')
@@ -46,7 +47,7 @@ class CleanupFiles:
# Metadata for tracking
metadata = {
'metadata': {
'pod_id': os.getenv('POD_ID'),
'pod_id': POD_ID,
'workflow_name': 'cleanup_files',
}
}

View File

@@ -17,6 +17,7 @@ with workflow.unsafe.imports_passed_through():
from typing import Any
from temporalio.common import RetryPolicy
from temporalio.exceptions import ApplicationError
from model_manager.activities.activities import Activities
from model_manager.activities.experiment_tracking import UpdateType
@@ -95,7 +96,11 @@ class TrainModel:
"""
workflow.logger.info(f'Starting train_model workflow for {input_data}')
experiment_run_id = self._validate_experiment_run_id(input_data)
try:
experiment_run_id = self._validate_experiment_run_id(input_data)
except ValueError as exc:
# Prevent workflow-task retries on deterministic input contract violations.
raise ApplicationError(str(exc), non_retryable=True) from exc
input_data = {**input_data, 'experiment_run_id': experiment_run_id}
model_name = input_data.get('model_name')