SIENTIAPDE-1773
Enhance environment configuration and update dependencies - Added new environment variables for PluginStore and MLflow configuration in `.env.example`, including `RUNTIME`, `STORE_BASE_URL`, `STORE_OWNER`, `STORE_REPO`, `STORE_BRANCH`, `STORE_USERNAME`, `STORE_PASSWORD`, `STORE_CACHE_TTL_SECONDS`, `PYPI_SERVER`, `PYPI_USERNAME`, and `PYPI_PASSWORD`. - Updated `git-requirements-mapping.txt` to reflect changes in repository names. - Modified `requirements-light.txt` and `requirements.txt` to upgrade `sientia-dataops-library` to version 1.12.0 and `sientia-mlops-library` to version 0.8.1. - Updated `values.yaml` to include new environment variables for worker runtime and PluginStore configuration. - Refactored E2E tests to utilize new MLflow repository stubs and PluginStore mocks for improved testing accuracy.
This commit is contained in:
@@ -50,8 +50,7 @@ base_input_data = {
|
||||
'prediction_store_policy': 'lts:1',
|
||||
'model_config': {
|
||||
'retention_minutes': 0,
|
||||
'transform_flavor': 'sklearn',
|
||||
'predict_flavor': 'sklearn',
|
||||
'target': 'sensor_1',
|
||||
},
|
||||
'datetime_columns': ['timestamp', 'created_at'],
|
||||
}
|
||||
@@ -80,24 +79,30 @@ def insert_sample_prediction(postgres_engine, model_id):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bad_data_model(patch_mlflow):
|
||||
model = MagicMock(predict=MagicMock(side_effect=Exception('Bad data model')))
|
||||
patch_mlflow.sklearn.load_model = MagicMock(return_value=model)
|
||||
return model
|
||||
def bad_data_model(mlflow_repository_stub):
|
||||
mlflow_repository_stub.stub_wrapper.transform = MagicMock(
|
||||
side_effect=Exception('Bad data model')
|
||||
)
|
||||
return mlflow_repository_stub.stub_wrapper
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bad_predict_model(patch_mlflow, mock_mlflow_models):
|
||||
model = MagicMock(predict=MagicMock(side_effect=Exception('Bad predict model')))
|
||||
def bad_predict_model(mlflow_repository_stub):
|
||||
wrapper = mlflow_repository_stub.stub_wrapper
|
||||
|
||||
def mock_sklearn_load_model(model_uri):
|
||||
if 'data_model' in model_uri or 'transform' in model_uri.lower():
|
||||
return mock_mlflow_models['transform_model']
|
||||
return model
|
||||
def _good_transform(data):
|
||||
result = pd.DataFrame(
|
||||
{
|
||||
'feature_1': [0.234] * len(data),
|
||||
'feature_2': [0.783] * len(data),
|
||||
}
|
||||
)
|
||||
result.index = data.index
|
||||
return result, {}
|
||||
|
||||
patch_mlflow.sklearn = MagicMock()
|
||||
patch_mlflow.sklearn.load_model = MagicMock(side_effect=mock_sklearn_load_model)
|
||||
return model
|
||||
wrapper.transform.side_effect = _good_transform
|
||||
wrapper.predict = MagicMock(side_effect=Exception('Bad predict model'))
|
||||
return wrapper
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -107,7 +112,7 @@ async def test_scenario_2_1_1_input_gate_triggers_continue(
|
||||
temporal_worker: Worker,
|
||||
test_activities: Activities,
|
||||
postgres_engine,
|
||||
mock_mlflow_models,
|
||||
mlflow_repository_stub,
|
||||
):
|
||||
"""Input gate CONTINUE: export default prediction; MLflow transform/predict not used."""
|
||||
client = temporal_test_env.client
|
||||
@@ -118,8 +123,8 @@ async def test_scenario_2_1_1_input_gate_triggers_continue(
|
||||
client, PredictionsBatch.run, input_data, make_workflow_id('test-continue-policy')
|
||||
)
|
||||
assert_continue(postgres_engine, model_id)
|
||||
mock_mlflow_models['transform_model'].predict.assert_not_called()
|
||||
mock_mlflow_models['predict_model'].predict.assert_not_called()
|
||||
mlflow_repository_stub.stub_wrapper.transform.assert_not_called()
|
||||
mlflow_repository_stub.stub_wrapper.predict.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -129,7 +134,7 @@ async def test_scenario_2_1_2_input_gate_triggers_stop(
|
||||
temporal_worker: Worker,
|
||||
test_activities: Activities,
|
||||
postgres_engine,
|
||||
mock_mlflow_models,
|
||||
mlflow_repository_stub,
|
||||
):
|
||||
"""Input gate STOP: no export, no MLflow."""
|
||||
client = temporal_test_env.client
|
||||
@@ -141,7 +146,7 @@ async def test_scenario_2_1_2_input_gate_triggers_stop(
|
||||
client, PredictionsBatch.run, input_data, make_workflow_id('test-input-stop')
|
||||
)
|
||||
assert_stop(postgres_engine, model_id)
|
||||
mock_mlflow_models['transform_model'].predict.assert_not_called()
|
||||
mlflow_repository_stub.stub_wrapper.transform.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -151,7 +156,7 @@ async def test_scenario_2_1_3_input_gate_triggers_repeat(
|
||||
temporal_worker: Worker,
|
||||
test_activities: Activities,
|
||||
postgres_engine,
|
||||
mock_mlflow_models,
|
||||
mlflow_repository_stub,
|
||||
):
|
||||
"""Input gate REPEAT with existing history."""
|
||||
client = temporal_test_env.client
|
||||
@@ -164,7 +169,7 @@ async def test_scenario_2_1_3_input_gate_triggers_repeat(
|
||||
client, PredictionsBatch.run, input_data, make_workflow_id('test-input-repeat')
|
||||
)
|
||||
assert_repeat(postgres_engine, model_id, data)
|
||||
mock_mlflow_models['transform_model'].predict.assert_not_called()
|
||||
mlflow_repository_stub.stub_wrapper.transform.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -222,7 +227,7 @@ async def test_scenario_2_2_2_transform_gate_triggers_stop(
|
||||
test_activities: Activities,
|
||||
postgres_engine,
|
||||
bad_data_model,
|
||||
mock_mlflow_models,
|
||||
mlflow_repository_stub,
|
||||
):
|
||||
client = temporal_test_env.client
|
||||
model_id = 222
|
||||
@@ -233,7 +238,7 @@ async def test_scenario_2_2_2_transform_gate_triggers_stop(
|
||||
client, PredictionsBatch.run, input_data, make_workflow_id('test-transform-stop')
|
||||
)
|
||||
assert_stop(postgres_engine, model_id)
|
||||
mock_mlflow_models['predict_model'].predict.assert_not_called()
|
||||
mlflow_repository_stub.stub_wrapper.predict.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -264,19 +269,20 @@ async def test_scenario_2_2_4_transform_content_gate_nan_values_stop(
|
||||
temporal_worker: Worker,
|
||||
test_activities: Activities,
|
||||
postgres_engine,
|
||||
mock_mlflow_models,
|
||||
mlflow_repository_stub,
|
||||
):
|
||||
"""mlflow_content_gate triggers STOP when transform output is all NaN (NAN_VALUES filter)."""
|
||||
client = temporal_test_env.client
|
||||
model_id = 224
|
||||
|
||||
def all_nan_transform(data):
|
||||
num_rows = max(len(data), 1) if hasattr(data, '__len__') else 1
|
||||
result = pd.DataFrame({'feature_1': [np.nan] * num_rows, 'feature_2': [np.nan] * num_rows})
|
||||
result = pd.DataFrame(
|
||||
{'feature_1': [np.nan] * len(data), 'feature_2': [np.nan] * len(data)}
|
||||
)
|
||||
result.index = data.index
|
||||
return result
|
||||
return result, {}
|
||||
|
||||
mock_mlflow_models['transform_model'].predict = MagicMock(side_effect=all_nan_transform)
|
||||
mlflow_repository_stub.stub_wrapper.transform = MagicMock(side_effect=all_nan_transform)
|
||||
|
||||
insert_sample_data(postgres_engine, model_id, [60.0, 78.2])
|
||||
input_data = get_base_input_data(model_id)
|
||||
@@ -288,7 +294,7 @@ async def test_scenario_2_2_4_transform_content_gate_nan_values_stop(
|
||||
client, PredictionsBatch.run, input_data, make_workflow_id('test-transform-content-stop')
|
||||
)
|
||||
assert_stop(postgres_engine, model_id)
|
||||
mock_mlflow_models['predict_model'].predict.assert_not_called()
|
||||
mlflow_repository_stub.stub_wrapper.predict.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user