From 2c63414770419ba97d3b081e886fb373931e3a45 Mon Sep 17 00:00:00 2001 From: vitor-aignosi Date: Thu, 18 Jun 2026 09:56:30 -0300 Subject: [PATCH] SIENTIAPDE-1645: Expose detailed model training information via a new Prometheus gauge. This gauge, sientia_training_info, records metadata such as dataset sizes, feature count, and evaluation metrics (MSE, MAE, R2) along with the training run's timestamp. --- model_manager/activities/training.py | 12 +++++++ model_manager/metrics.py | 16 ++++++++++ tests/test_metrics.py | 48 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/model_manager/activities/training.py b/model_manager/activities/training.py index a6cc284..eb50a4f 100644 --- a/model_manager/activities/training.py +++ b/model_manager/activities/training.py @@ -262,6 +262,18 @@ class Training(SientiaMonitoring): train_result.r2_val ) + mm_metrics.SIENTIA_TRAINING_INFO.labels( + pod_id=labels['pod_id'], + model_name=train_params.model_name, + model_type=train_params.model_type, + dataset_train_rows=str(len(train_result.train_data)), + dataset_val_rows=str(len(train_result.val_data)), + feature_count=str(len(train_params.variable_columns)), + mse=str(train_result.mse_val) if train_result.mse_val is not None else '', + mae=str(train_result.mae_val) if train_result.mae_val is not None else '', + r2=str(train_result.r2_val) if train_result.r2_val is not None else '', + ).set(time.time() * 1000) + self.info(f'Starting MLflow run for {train_params.model_type}', metadata) with self.mlflow_repository.start_run( model_name=train_params.model_name, diff --git a/model_manager/metrics.py b/model_manager/metrics.py index cc52ac5..ed11437 100644 --- a/model_manager/metrics.py +++ b/model_manager/metrics.py @@ -27,6 +27,22 @@ APP_UP = Gauge( _TRAINING_LABELS = ['pod_id', 'model_name', 'model_type'] +SIENTIA_TRAINING_INFO = Gauge( + 'sientia_training_info', + 'Metadata and execution timestamp (ms) of the last successful model training run', + [ + 'pod_id', + 'model_name', + 'model_type', + 'dataset_train_rows', + 'dataset_val_rows', + 'feature_count', + 'mse', + 'mae', + 'r2', + ], +) + SIENTIA_TRAINING_MODEL_TRAINED_TOTAL = Counter( 'sientia_training_model_trained_total', 'Number of successfully completed model training runs', diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 3e47dec..2060902 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -329,3 +329,51 @@ def test_sientia_training_feature_count_is_gauge(): assert isinstance(SIENTIA_TRAINING_FEATURE_COUNT, Gauge) assert SIENTIA_TRAINING_FEATURE_COUNT._name == 'sientia_training_feature_count' _assert_training_labels(SIENTIA_TRAINING_FEATURE_COUNT) + + +def test_sientia_training_info_is_gauge(): + from prometheus_client import Gauge + + from model_manager.metrics import SIENTIA_TRAINING_INFO + + assert isinstance(SIENTIA_TRAINING_INFO, Gauge) + assert SIENTIA_TRAINING_INFO._name == 'sientia_training_info' + + expected_labels = { + 'pod_id', 'model_name', 'model_type', + 'dataset_train_rows', 'dataset_val_rows', 'feature_count', + 'mse', 'mae', 'r2', + } + assert expected_labels == set(SIENTIA_TRAINING_INFO._labelnames) + + +def test_sientia_training_info_set_value(): + import time + + from model_manager.metrics import SIENTIA_TRAINING_INFO + + ts = time.time() * 1000 + SIENTIA_TRAINING_INFO.labels( + pod_id='test-pod', + model_name='my_model', + model_type='linear', + dataset_train_rows='1000', + dataset_val_rows='200', + feature_count='5', + mse='0.01', + mae='0.08', + r2='0.95', + ).set(ts) + + value = SIENTIA_TRAINING_INFO.labels( + pod_id='test-pod', + model_name='my_model', + model_type='linear', + dataset_train_rows='1000', + dataset_val_rows='200', + feature_count='5', + mse='0.01', + mae='0.08', + r2='0.95', + )._value._value + assert abs(value - ts) < 2000