Repository: SeldonIO/MLServer

Issue:
Add "don't log" header to Alibi runtime



Discussion:

Resolving patch:
diff --git a/runtimes/alibi-explain/mlserver_alibi_explain/common.py b/runtimes/alibi-explain/mlserver_alibi_explain/common.py
--- a/runtimes/alibi-explain/mlserver_alibi_explain/common.py
+++ b/runtimes/alibi-explain/mlserver_alibi_explain/common.py
@@ -26,6 +26,7 @@
 
 ENV_PREFIX_ALIBI_EXPLAIN_SETTINGS = "MLSERVER_MODEL_ALIBI_EXPLAIN_"
 EXPLAIN_PARAMETERS_TAG = "explain_parameters"
+SELDON_SKIP_LOGGING_HEADER = "Seldon-Skip-Logging"
 
 
 #  TODO: add this utility in the codec.
@@ -54,7 +55,12 @@ def remote_predict(
     verify: Union[str, bool] = True
     if ssl_verify_path != "":
         verify = ssl_verify_path
-    response_raw = requests.post(predictor_url, json=v2_payload.dict(), verify=verify)
+    response_raw = requests.post(
+        predictor_url,
+        json=v2_payload.dict(),
+        headers={SELDON_SKIP_LOGGING_HEADER: "true"},
+        verify=verify,
+    )
     if response_raw.status_code != 200:
         raise RemoteInferenceError(response_raw.status_code, response_raw.reason)
     return InferenceResponse.parse_raw(response_raw.text)


Test patch:
diff --git a/runtimes/alibi-explain/tests/conftest.py b/runtimes/alibi-explain/tests/conftest.py
--- a/runtimes/alibi-explain/tests/conftest.py
+++ b/runtimes/alibi-explain/tests/conftest.py
@@ -153,29 +153,36 @@ def anchor_image_directory() -> Path:
 
 
 @pytest.fixture
-async def anchor_image_runtime_with_remote_predict_patch(
-    anchor_image_directory, custom_runtime_tf: MLModel, mocker
-) -> AsyncIterable[AlibiExplainRuntime]:
-    def _mock_metadata(*args, **kwargs):
-        return MetadataModelResponse(name="dummy", platform="dummy")
-
+def mock_remote_predict(mocker, custom_runtime_tf: MLModel):
     def _mock_predict(*args, **kwargs):
         # mock implementation is required as we dont want to spin up a server,
         # we just use MLModel.predict
         return run_async_as_sync(custom_runtime_tf.predict, kwargs["v2_payload"])
 
-    remote_predict = mocker.patch(
-        "mlserver_alibi_explain.explainers.black_box_runtime.remote_predict"
-    )
-    remote_metadata = mocker.patch(
-        "mlserver_alibi_explain.explainers.black_box_runtime.remote_metadata"
+    return mocker.patch(
+        "mlserver_alibi_explain.explainers.black_box_runtime.remote_predict",
+        side_effect=functools.partial(_mock_predict, runtime=custom_runtime_tf),
     )
 
-    remote_predict.side_effect = functools.partial(
-        _mock_predict, runtime=custom_runtime_tf
+
+@pytest.fixture
+def mock_remote_metadata(mocker):
+    def _mock_metadata(*args, **kwargs):
+        return MetadataModelResponse(name="dummy", platform="dummy")
+
+    return mocker.patch(
+        "mlserver_alibi_explain.explainers.black_box_runtime.remote_metadata",
+        side_effect=_mock_metadata,
     )
-    remote_metadata.side_effect = _mock_metadata
 
+
+@pytest.fixture
+async def anchor_image_runtime_with_remote_predict_patch(
+    anchor_image_directory,
+    custom_runtime_tf: MLModel,
+    mock_remote_metadata,
+    mock_remote_predict,
+) -> AsyncIterable[AlibiExplainRuntime]:
     rt = AlibiExplainRuntime(
         ModelSettings(
             parallel_workers=0,
diff --git a/runtimes/alibi-explain/tests/test_common.py b/runtimes/alibi-explain/tests/test_common.py
--- a/runtimes/alibi-explain/tests/test_common.py
+++ b/runtimes/alibi-explain/tests/test_common.py
@@ -1,8 +1,17 @@
 import pytest
 
-from mlserver.types import ResponseOutput
+from mlserver.types import (
+    InferenceRequest,
+    InferenceResponse,
+    RequestInput,
+    ResponseOutput,
+)
 
-from mlserver_alibi_explain.common import convert_from_bytes
+from mlserver_alibi_explain.common import (
+    convert_from_bytes,
+    remote_predict,
+    SELDON_SKIP_LOGGING_HEADER,
+)
 from mlserver_alibi_explain.errors import InvalidExplanationShape
 
 
@@ -16,3 +25,24 @@
 def test_convert_from_bytes_invalid(output: ResponseOutput):
     with pytest.raises(InvalidExplanationShape):
         convert_from_bytes(output)
+
+
+def test_remote_predict(requests_mock):
+    uri = "mock://v2/models/foo/infer"
+
+    inference_request = InferenceRequest(
+        inputs=[RequestInput(name="bar", datatype="INT32", shape=[1, 2], data=[1, 2])]
+    )
+    inference_response = InferenceResponse(
+        model_name="foo",
+        outputs=[ResponseOutput(name="bar2", datatype="FP32", shape=[1, 1], data=[3])],
+    )
+
+    requests_mock.post(
+        uri,
+        text=inference_response.json(),
+        request_headers={SELDON_SKIP_LOGGING_HEADER: "true"},
+    )
+    res = remote_predict(inference_request, uri, True)
+
+    assert res == inference_response