We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Install requirements system-wide when building custom images
Currently, custom dependencies coming from a `requirements.txt` get installed on the base environment for user `1000` (i.e. the default non-`root` user in the image). Therefore, if the image is loaded with a different user ID, these custom dependencies are not available.

To work around this, we should install these custom deps under `root`, so that they are installed system-wide and accessible by any user.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `runtimes/sklearn/mlserver_sklearn/encoding.py`
Content:
```
1 from dataclasses import dataclass
2 from typing import List, Union
3 
4 import numpy as np
5 import pandas as pd
6 
7 from mlserver.codecs import NumpyCodec, PandasCodec
8 from mlserver.errors import InferenceError
9 from mlserver.types import ResponseOutput
10 
11 # TODO: more support!
12 SKLearnResponse = Union[np.ndarray, pd.DataFrame]
13 
14 
15 @dataclass
16 class SKLearnPayload:
17     """Class for keeping track of requested outputs
18     and corresponding model responses."""
19 
20     requested_output: ResponseOutput
21     model_output: SKLearnResponse
22 
23 
24 def to_outputs(sklearn_payloads: List[SKLearnPayload]) -> List[ResponseOutput]:
25     """
26     Encodes a list of SKLearn payloads into a list of proto-able ResponseOutputs.
27 
28     :param sklearn_payloads: List of requested outputs + the responses from the
29         SKLearn model
30     :return: response_outputs: List of encoded response outputs
31     :raises: InferenceError if multiple columnar responses were returned by the model
32     """
33     response_outputs = []
34 
35     all_output_names = [p.requested_output.name for p in sklearn_payloads]
36 
37     for payload in sklearn_payloads:
38         if _is_columnar_data(payload) and len(sklearn_payloads) > 1:
39             raise InferenceError(
40                 f"{payload.requested_output.name} returned columnar data of type"
41                 f" {type(payload.model_output)} and {all_output_names} were"
42                 f" requested. Cannot encode multiple columnar data responses"
43                 f" one response."
44             )
45 
46         if isinstance(payload.model_output, pd.DataFrame):
47             # Immediately return the outputs of columnar data encoding,
48             # don't try to jam more outputs together in one response.
49             return PandasCodec.encode("some-model", payload.model_output).outputs
50 
51         response_output = NumpyCodec.encode(
52             name=payload.requested_output.name, payload=payload.model_output
53         )
54         response_outputs.append(response_output)
55 
56     return response_outputs
57 
58 
59 def _is_columnar_data(payload: SKLearnPayload) -> bool:
60     return isinstance(payload.model_output, pd.DataFrame)
61
```

Path: `runtimes/sklearn/mlserver_sklearn/sklearn.py`
Content:
```
1 from typing import List
2 
3 import joblib
4 from mlserver_sklearn import encoding
5 from mlserver_sklearn.encoding import SKLearnPayload
6 from sklearn.pipeline import Pipeline
7 
8 from mlserver import types
9 from mlserver.codecs import NumpyRequestCodec
10 from mlserver.errors import InferenceError
11 from mlserver.model import MLModel
12 from mlserver.types import InferenceResponse
13 from mlserver.utils import get_model_uri
14 
15 PREDICT_OUTPUT = "predict"
16 PREDICT_PROBA_OUTPUT = "predict_proba"
17 VALID_OUTPUTS = [PREDICT_OUTPUT, PREDICT_PROBA_OUTPUT]
18 
19 WELLKNOWN_MODEL_FILENAMES = ["model.joblib", "model.pickle", "model.pkl"]
20 
21 
22 class SKLearnModel(MLModel):
23     """
24     Implementation of the MLModel interface to load and serve `scikit-learn`
25     models persisted with `joblib`.
26     """
27 
28     async def load(self) -> bool:
29         # TODO: Log info message
30         model_uri = await get_model_uri(
31             self._settings, wellknown_filenames=WELLKNOWN_MODEL_FILENAMES
32         )
33         self._model = joblib.load(model_uri)
34 
35         self.ready = True
36         return self.ready
37 
38     async def predict(self, payload: types.InferenceRequest) -> types.InferenceResponse:
39         payload = self._check_request(payload)
40 
41         model_responses = self._get_model_outputs(payload)
42 
43         return InferenceResponse(
44             model_name=self.name,
45             model_version=self.version,
46             outputs=encoding.to_outputs(sklearn_payloads=model_responses),
47         )
48 
49     def _check_request(self, payload: types.InferenceRequest) -> types.InferenceRequest:
50         if not payload.outputs:
51             # By default, only return the result of `predict()`
52             payload.outputs = [types.RequestOutput(name=PREDICT_OUTPUT)]
53         else:
54             for request_output in payload.outputs:
55                 if request_output.name not in VALID_OUTPUTS:
56                     raise InferenceError(
57                         f"SKLearnModel only supports '{PREDICT_OUTPUT}' and "
58                         f"'{PREDICT_PROBA_OUTPUT}' as outputs "
59                         f"({request_output.name} was received)"
60                     )
61 
62         # Regression models do not support `predict_proba`
63         if PREDICT_PROBA_OUTPUT in [o.name for o in payload.outputs]:
64             # Ensure model supports it
65             maybe_regressor = self._model
66             if isinstance(self._model, Pipeline):
67                 maybe_regressor = maybe_regressor.steps[-1][-1]
68 
69             if not hasattr(maybe_regressor, PREDICT_PROBA_OUTPUT):
70                 raise InferenceError(
71                     f"{type(maybe_regressor)} models do not support "
72                     f"'{PREDICT_PROBA_OUTPUT}"
73                 )
74 
75         return payload
76 
77     def _get_model_outputs(
78         self, payload: types.InferenceRequest
79     ) -> List[SKLearnPayload]:
80         decoded_request = self.decode_request(payload, default_codec=NumpyRequestCodec)
81 
82         outputs = []
83         for request_output in payload.outputs:  # type: ignore
84             predict_fn = getattr(self._model, request_output.name)
85             y = predict_fn(decoded_request)
86             payload = SKLearnPayload(requested_output=request_output, model_output=y)
87             outputs.append(payload)
88 
89         return outputs
90
```

Path: `mlserver/cli/constants.py`
Content:
```
1 DockerfileName = "Dockerfile"
2 DockerfileTemplate = """
3 FROM continuumio/miniconda3:4.10.3 AS env-builder
4 SHELL ["/bin/bash", "-c"]
5 
6 ARG MLSERVER_ENV_NAME="mlserver-custom-env" \\
7     MLSERVER_ENV_TARBALL="./envs/base.tar.gz"
8 
9 RUN conda config --add channels conda-forge && \\
10     conda install conda-pack
11 
12 # The `[]` character range will ensure that Docker doesn't complain if the
13 # files don't exist:
14 # https://stackoverflow.com/a/65138098/5015573
15 COPY \\
16     ./environment.ym[l] \\
17     ./environment.yam[l] \\
18     ./conda.ym[l] \\
19     ./conda.yam[l] \\
20     .
21 RUN mkdir $(dirname $MLSERVER_ENV_TARBALL); \\
22     for envFile in environment.yml environment.yaml conda.yml conda.yaml; do \\
23         if [[ -f $envFile ]]; then \\
24             conda env create \
25                 --name $MLSERVER_ENV_NAME \\
26                 --file $envFile; \\
27             conda-pack \
28                 -n $MLSERVER_ENV_NAME \\
29                 -o $MLSERVER_ENV_TARBALL; \\
30         fi \\
31     done; \\
32     chmod -R 776 $(dirname $MLSERVER_ENV_TARBALL)
33 
34 FROM seldonio/mlserver:{version}-slim
35 SHELL ["/bin/bash", "-c"]
36 
37 # Copy all potential sources for custom environments
38 COPY \\
39     --chown=1000 \\
40     --from=env-builder \\
41     /envs/base.tar.g[z] \\
42     ./envs/base.tar.gz
43 COPY \\
44     ./settings.jso[n] \\
45     ./model-settings.jso[n] \\
46     ./requirements.tx[t] \\
47     .
48 
49 RUN ./hack/build-env.sh . ./envs/base
50 
51 # Copy everything else
52 COPY . .
53 
54 # Override MLServer's own `CMD` to activate the embedded environment
55 # (optionally activating the hot-loaded one as well).
56 CMD source ./hack/activate-env.sh ./envs/base.tar.gz ./envs/base && \\
57     mlserver start $MLSERVER_MODELS_DIR
58 """
59 
60 DockerignoreName = ".dockerignore"
61 Dockerignore = """
62 # Binaries for programs and plugins
63 *.exe
64 *.exe~
65 *.dll
66 *.so
67 *.dylib
68 *.pyc
69 *.pyo
70 *.pyd
71 bin
72 
73 # Mac file system
74 **/.DS_Store
75 
76 # Python dev
77 __pycache__
78 .Python
79 env
80 pip-log.txt
81 pip-delete-this-directory.txt
82 .mypy_cache
83 eggs/
84 .eggs/
85 *.egg-info/
86 ./pytest_cache
87 .tox
88 build/
89 dist/
90 
91 # Notebook Checkpoints
92 .ipynb_checkpoints
93 
94 .coverage
95 .coverage.*
96 .cache
97 nosetests.xml
98 coverage.xml
99 *,cover
100 *.log
101 .git
102 """
103
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mlserver/cli/constants.py b/mlserver/cli/constants.py
--- a/mlserver/cli/constants.py
+++ b/mlserver/cli/constants.py
@@ -46,7 +46,13 @@
     ./requirements.tx[t] \\
     .
 
-RUN ./hack/build-env.sh . ./envs/base
+USER root
+# Install dependencies system-wide, to ensure that they are available for every
+# user
+RUN ./hack/build-env.sh . ./envs/base && \
+    chown -R 1000:0 ./envs/base && \\
+    chmod -R 776 ./envs/base
+USER 1000
 
 # Copy everything else
 COPY . .
diff --git a/runtimes/sklearn/mlserver_sklearn/encoding.py b/runtimes/sklearn/mlserver_sklearn/encoding.py
--- a/runtimes/sklearn/mlserver_sklearn/encoding.py
+++ b/runtimes/sklearn/mlserver_sklearn/encoding.py
@@ -6,7 +6,7 @@
 
 from mlserver.codecs import NumpyCodec, PandasCodec
 from mlserver.errors import InferenceError
-from mlserver.types import ResponseOutput
+from mlserver.types import RequestOutput, ResponseOutput
 
 # TODO: more support!
 SKLearnResponse = Union[np.ndarray, pd.DataFrame]
@@ -17,7 +17,7 @@
     """Class for keeping track of requested outputs
     and corresponding model responses."""
 
-    requested_output: ResponseOutput
+    requested_output: RequestOutput
     model_output: SKLearnResponse
 
 
diff --git a/runtimes/sklearn/mlserver_sklearn/sklearn.py b/runtimes/sklearn/mlserver_sklearn/sklearn.py
--- a/runtimes/sklearn/mlserver_sklearn/sklearn.py
+++ b/runtimes/sklearn/mlserver_sklearn/sklearn.py
@@ -83,7 +83,7 @@
         for request_output in payload.outputs:  # type: ignore
             predict_fn = getattr(self._model, request_output.name)
             y = predict_fn(decoded_request)
-            payload = SKLearnPayload(requested_output=request_output, model_output=y)
-            outputs.append(payload)
+            output = SKLearnPayload(requested_output=request_output, model_output=y)
+            outputs.append(output)
 
         return outputs

{"golden_diff": "diff --git a/mlserver/cli/constants.py b/mlserver/cli/constants.py\n--- a/mlserver/cli/constants.py\n+++ b/mlserver/cli/constants.py\n@@ -46,7 +46,13 @@\n     ./requirements.tx[t] \\\\\n     .\n \n-RUN ./hack/build-env.sh . ./envs/base\n+USER root\n+# Install dependencies system-wide, to ensure that they are available for every\n+# user\n+RUN ./hack/build-env.sh . ./envs/base && \\\n+    chown -R 1000:0 ./envs/base && \\\\\n+    chmod -R 776 ./envs/base\n+USER 1000\n \n # Copy everything else\n COPY . .\ndiff --git a/runtimes/sklearn/mlserver_sklearn/encoding.py b/runtimes/sklearn/mlserver_sklearn/encoding.py\n--- a/runtimes/sklearn/mlserver_sklearn/encoding.py\n+++ b/runtimes/sklearn/mlserver_sklearn/encoding.py\n@@ -6,7 +6,7 @@\n \n from mlserver.codecs import NumpyCodec, PandasCodec\n from mlserver.errors import InferenceError\n-from mlserver.types import ResponseOutput\n+from mlserver.types import RequestOutput, ResponseOutput\n \n # TODO: more support!\n SKLearnResponse = Union[np.ndarray, pd.DataFrame]\n@@ -17,7 +17,7 @@\n     \"\"\"Class for keeping track of requested outputs\n     and corresponding model responses.\"\"\"\n \n-    requested_output: ResponseOutput\n+    requested_output: RequestOutput\n     model_output: SKLearnResponse\n \n \ndiff --git a/runtimes/sklearn/mlserver_sklearn/sklearn.py b/runtimes/sklearn/mlserver_sklearn/sklearn.py\n--- a/runtimes/sklearn/mlserver_sklearn/sklearn.py\n+++ b/runtimes/sklearn/mlserver_sklearn/sklearn.py\n@@ -83,7 +83,7 @@\n         for request_output in payload.outputs:  # type: ignore\n             predict_fn = getattr(self._model, request_output.name)\n             y = predict_fn(decoded_request)\n-            payload = SKLearnPayload(requested_output=request_output, model_output=y)\n-            outputs.append(payload)\n+            output = SKLearnPayload(requested_output=request_output, model_output=y)\n+            outputs.append(output)\n \n         return outputs\n", "issue": "Install requirements system-wide when building custom images\nCurrently, custom dependencies coming from a `requirements.txt` get installed on the base environment for user `1000` (i.e. the default non-`root` user in the image). Therefore, if the image is loaded with a different user ID, these custom dependencies are not available.\r\n\r\nTo work around this, we should install these custom deps under `root`, so that they are installed system-wide and accessible by any user.\n", "before_files": [{"content": "from dataclasses import dataclass\nfrom typing import List, Union\n\nimport numpy as np\nimport pandas as pd\n\nfrom mlserver.codecs import NumpyCodec, PandasCodec\nfrom mlserver.errors import InferenceError\nfrom mlserver.types import ResponseOutput\n\n# TODO: more support!\nSKLearnResponse = Union[np.ndarray, pd.DataFrame]\n\n\n@dataclass\nclass SKLearnPayload:\n    \"\"\"Class for keeping track of requested outputs\n    and corresponding model responses.\"\"\"\n\n    requested_output: ResponseOutput\n    model_output: SKLearnResponse\n\n\ndef to_outputs(sklearn_payloads: List[SKLearnPayload]) -> List[ResponseOutput]:\n    \"\"\"\n    Encodes a list of SKLearn payloads into a list of proto-able ResponseOutputs.\n\n    :param sklearn_payloads: List of requested outputs + the responses from the\n        SKLearn model\n    :return: response_outputs: List of encoded response outputs\n    :raises: InferenceError if multiple columnar responses were returned by the model\n    \"\"\"\n    response_outputs = []\n\n    all_output_names = [p.requested_output.name for p in sklearn_payloads]\n\n    for payload in sklearn_payloads:\n        if _is_columnar_data(payload) and len(sklearn_payloads) > 1:\n            raise InferenceError(\n                f\"{payload.requested_output.name} returned columnar data of type\"\n                f\" {type(payload.model_output)} and {all_output_names} were\"\n                f\" requested. Cannot encode multiple columnar data responses\"\n                f\" one response.\"\n            )\n\n        if isinstance(payload.model_output, pd.DataFrame):\n            # Immediately return the outputs of columnar data encoding,\n            # don't try to jam more outputs together in one response.\n            return PandasCodec.encode(\"some-model\", payload.model_output).outputs\n\n        response_output = NumpyCodec.encode(\n            name=payload.requested_output.name, payload=payload.model_output\n        )\n        response_outputs.append(response_output)\n\n    return response_outputs\n\n\ndef _is_columnar_data(payload: SKLearnPayload) -> bool:\n    return isinstance(payload.model_output, pd.DataFrame)\n", "path": "runtimes/sklearn/mlserver_sklearn/encoding.py"}, {"content": "from typing import List\n\nimport joblib\nfrom mlserver_sklearn import encoding\nfrom mlserver_sklearn.encoding import SKLearnPayload\nfrom sklearn.pipeline import Pipeline\n\nfrom mlserver import types\nfrom mlserver.codecs import NumpyRequestCodec\nfrom mlserver.errors import InferenceError\nfrom mlserver.model import MLModel\nfrom mlserver.types import InferenceResponse\nfrom mlserver.utils import get_model_uri\n\nPREDICT_OUTPUT = \"predict\"\nPREDICT_PROBA_OUTPUT = \"predict_proba\"\nVALID_OUTPUTS = [PREDICT_OUTPUT, PREDICT_PROBA_OUTPUT]\n\nWELLKNOWN_MODEL_FILENAMES = [\"model.joblib\", \"model.pickle\", \"model.pkl\"]\n\n\nclass SKLearnModel(MLModel):\n    \"\"\"\n    Implementation of the MLModel interface to load and serve `scikit-learn`\n    models persisted with `joblib`.\n    \"\"\"\n\n    async def load(self) -> bool:\n        # TODO: Log info message\n        model_uri = await get_model_uri(\n            self._settings, wellknown_filenames=WELLKNOWN_MODEL_FILENAMES\n        )\n        self._model = joblib.load(model_uri)\n\n        self.ready = True\n        return self.ready\n\n    async def predict(self, payload: types.InferenceRequest) -> types.InferenceResponse:\n        payload = self._check_request(payload)\n\n        model_responses = self._get_model_outputs(payload)\n\n        return InferenceResponse(\n            model_name=self.name,\n            model_version=self.version,\n            outputs=encoding.to_outputs(sklearn_payloads=model_responses),\n        )\n\n    def _check_request(self, payload: types.InferenceRequest) -> types.InferenceRequest:\n        if not payload.outputs:\n            # By default, only return the result of `predict()`\n            payload.outputs = [types.RequestOutput(name=PREDICT_OUTPUT)]\n        else:\n            for request_output in payload.outputs:\n                if request_output.name not in VALID_OUTPUTS:\n                    raise InferenceError(\n                        f\"SKLearnModel only supports '{PREDICT_OUTPUT}' and \"\n                        f\"'{PREDICT_PROBA_OUTPUT}' as outputs \"\n                        f\"({request_output.name} was received)\"\n                    )\n\n        # Regression models do not support `predict_proba`\n        if PREDICT_PROBA_OUTPUT in [o.name for o in payload.outputs]:\n            # Ensure model supports it\n            maybe_regressor = self._model\n            if isinstance(self._model, Pipeline):\n                maybe_regressor = maybe_regressor.steps[-1][-1]\n\n            if not hasattr(maybe_regressor, PREDICT_PROBA_OUTPUT):\n                raise InferenceError(\n                    f\"{type(maybe_regressor)} models do not support \"\n                    f\"'{PREDICT_PROBA_OUTPUT}\"\n                )\n\n        return payload\n\n    def _get_model_outputs(\n        self, payload: types.InferenceRequest\n    ) -> List[SKLearnPayload]:\n        decoded_request = self.decode_request(payload, default_codec=NumpyRequestCodec)\n\n        outputs = []\n        for request_output in payload.outputs:  # type: ignore\n            predict_fn = getattr(self._model, request_output.name)\n            y = predict_fn(decoded_request)\n            payload = SKLearnPayload(requested_output=request_output, model_output=y)\n            outputs.append(payload)\n\n        return outputs\n", "path": "runtimes/sklearn/mlserver_sklearn/sklearn.py"}, {"content": "DockerfileName = \"Dockerfile\"\nDockerfileTemplate = \"\"\"\nFROM continuumio/miniconda3:4.10.3 AS env-builder\nSHELL [\"/bin/bash\", \"-c\"]\n\nARG MLSERVER_ENV_NAME=\"mlserver-custom-env\" \\\\\n    MLSERVER_ENV_TARBALL=\"./envs/base.tar.gz\"\n\nRUN conda config --add channels conda-forge && \\\\\n    conda install conda-pack\n\n# The `[]` character range will ensure that Docker doesn't complain if the\n# files don't exist:\n# https://stackoverflow.com/a/65138098/5015573\nCOPY \\\\\n    ./environment.ym[l] \\\\\n    ./environment.yam[l] \\\\\n    ./conda.ym[l] \\\\\n    ./conda.yam[l] \\\\\n    .\nRUN mkdir $(dirname $MLSERVER_ENV_TARBALL); \\\\\n    for envFile in environment.yml environment.yaml conda.yml conda.yaml; do \\\\\n        if [[ -f $envFile ]]; then \\\\\n            conda env create \\\n                --name $MLSERVER_ENV_NAME \\\\\n                --file $envFile; \\\\\n            conda-pack \\\n                -n $MLSERVER_ENV_NAME \\\\\n                -o $MLSERVER_ENV_TARBALL; \\\\\n        fi \\\\\n    done; \\\\\n    chmod -R 776 $(dirname $MLSERVER_ENV_TARBALL)\n\nFROM seldonio/mlserver:{version}-slim\nSHELL [\"/bin/bash\", \"-c\"]\n\n# Copy all potential sources for custom environments\nCOPY \\\\\n    --chown=1000 \\\\\n    --from=env-builder \\\\\n    /envs/base.tar.g[z] \\\\\n    ./envs/base.tar.gz\nCOPY \\\\\n    ./settings.jso[n] \\\\\n    ./model-settings.jso[n] \\\\\n    ./requirements.tx[t] \\\\\n    .\n\nRUN ./hack/build-env.sh . ./envs/base\n\n# Copy everything else\nCOPY . .\n\n# Override MLServer's own `CMD` to activate the embedded environment\n# (optionally activating the hot-loaded one as well).\nCMD source ./hack/activate-env.sh ./envs/base.tar.gz ./envs/base && \\\\\n    mlserver start $MLSERVER_MODELS_DIR\n\"\"\"\n\nDockerignoreName = \".dockerignore\"\nDockerignore = \"\"\"\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n*.pyc\n*.pyo\n*.pyd\nbin\n\n# Mac file system\n**/.DS_Store\n\n# Python dev\n__pycache__\n.Python\nenv\npip-log.txt\npip-delete-this-directory.txt\n.mypy_cache\neggs/\n.eggs/\n*.egg-info/\n./pytest_cache\n.tox\nbuild/\ndist/\n\n# Notebook Checkpoints\n.ipynb_checkpoints\n\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n*.log\n.git\n\"\"\"\n", "path": "mlserver/cli/constants.py"}], "after_files": [{"content": "from dataclasses import dataclass\nfrom typing import List, Union\n\nimport numpy as np\nimport pandas as pd\n\nfrom mlserver.codecs import NumpyCodec, PandasCodec\nfrom mlserver.errors import InferenceError\nfrom mlserver.types import RequestOutput, ResponseOutput\n\n# TODO: more support!\nSKLearnResponse = Union[np.ndarray, pd.DataFrame]\n\n\n@dataclass\nclass SKLearnPayload:\n    \"\"\"Class for keeping track of requested outputs\n    and corresponding model responses.\"\"\"\n\n    requested_output: RequestOutput\n    model_output: SKLearnResponse\n\n\ndef to_outputs(sklearn_payloads: List[SKLearnPayload]) -> List[ResponseOutput]:\n    \"\"\"\n    Encodes a list of SKLearn payloads into a list of proto-able ResponseOutputs.\n\n    :param sklearn_payloads: List of requested outputs + the responses from the\n        SKLearn model\n    :return: response_outputs: List of encoded response outputs\n    :raises: InferenceError if multiple columnar responses were returned by the model\n    \"\"\"\n    response_outputs = []\n\n    all_output_names = [p.requested_output.name for p in sklearn_payloads]\n\n    for payload in sklearn_payloads:\n        if _is_columnar_data(payload) and len(sklearn_payloads) > 1:\n            raise InferenceError(\n                f\"{payload.requested_output.name} returned columnar data of type\"\n                f\" {type(payload.model_output)} and {all_output_names} were\"\n                f\" requested. Cannot encode multiple columnar data responses\"\n                f\" one response.\"\n            )\n\n        if isinstance(payload.model_output, pd.DataFrame):\n            # Immediately return the outputs of columnar data encoding,\n            # don't try to jam more outputs together in one response.\n            return PandasCodec.encode(\"some-model\", payload.model_output).outputs\n\n        response_output = NumpyCodec.encode(\n            name=payload.requested_output.name, payload=payload.model_output\n        )\n        response_outputs.append(response_output)\n\n    return response_outputs\n\n\ndef _is_columnar_data(payload: SKLearnPayload) -> bool:\n    return isinstance(payload.model_output, pd.DataFrame)\n", "path": "runtimes/sklearn/mlserver_sklearn/encoding.py"}, {"content": "from typing import List\n\nimport joblib\nfrom mlserver_sklearn import encoding\nfrom mlserver_sklearn.encoding import SKLearnPayload\nfrom sklearn.pipeline import Pipeline\n\nfrom mlserver import types\nfrom mlserver.codecs import NumpyRequestCodec\nfrom mlserver.errors import InferenceError\nfrom mlserver.model import MLModel\nfrom mlserver.types import InferenceResponse\nfrom mlserver.utils import get_model_uri\n\nPREDICT_OUTPUT = \"predict\"\nPREDICT_PROBA_OUTPUT = \"predict_proba\"\nVALID_OUTPUTS = [PREDICT_OUTPUT, PREDICT_PROBA_OUTPUT]\n\nWELLKNOWN_MODEL_FILENAMES = [\"model.joblib\", \"model.pickle\", \"model.pkl\"]\n\n\nclass SKLearnModel(MLModel):\n    \"\"\"\n    Implementation of the MLModel interface to load and serve `scikit-learn`\n    models persisted with `joblib`.\n    \"\"\"\n\n    async def load(self) -> bool:\n        # TODO: Log info message\n        model_uri = await get_model_uri(\n            self._settings, wellknown_filenames=WELLKNOWN_MODEL_FILENAMES\n        )\n        self._model = joblib.load(model_uri)\n\n        self.ready = True\n        return self.ready\n\n    async def predict(self, payload: types.InferenceRequest) -> types.InferenceResponse:\n        payload = self._check_request(payload)\n\n        model_responses = self._get_model_outputs(payload)\n\n        return InferenceResponse(\n            model_name=self.name,\n            model_version=self.version,\n            outputs=encoding.to_outputs(sklearn_payloads=model_responses),\n        )\n\n    def _check_request(self, payload: types.InferenceRequest) -> types.InferenceRequest:\n        if not payload.outputs:\n            # By default, only return the result of `predict()`\n            payload.outputs = [types.RequestOutput(name=PREDICT_OUTPUT)]\n        else:\n            for request_output in payload.outputs:\n                if request_output.name not in VALID_OUTPUTS:\n                    raise InferenceError(\n                        f\"SKLearnModel only supports '{PREDICT_OUTPUT}' and \"\n                        f\"'{PREDICT_PROBA_OUTPUT}' as outputs \"\n                        f\"({request_output.name} was received)\"\n                    )\n\n        # Regression models do not support `predict_proba`\n        if PREDICT_PROBA_OUTPUT in [o.name for o in payload.outputs]:\n            # Ensure model supports it\n            maybe_regressor = self._model\n            if isinstance(self._model, Pipeline):\n                maybe_regressor = maybe_regressor.steps[-1][-1]\n\n            if not hasattr(maybe_regressor, PREDICT_PROBA_OUTPUT):\n                raise InferenceError(\n                    f\"{type(maybe_regressor)} models do not support \"\n                    f\"'{PREDICT_PROBA_OUTPUT}\"\n                )\n\n        return payload\n\n    def _get_model_outputs(\n        self, payload: types.InferenceRequest\n    ) -> List[SKLearnPayload]:\n        decoded_request = self.decode_request(payload, default_codec=NumpyRequestCodec)\n\n        outputs = []\n        for request_output in payload.outputs:  # type: ignore\n            predict_fn = getattr(self._model, request_output.name)\n            y = predict_fn(decoded_request)\n            output = SKLearnPayload(requested_output=request_output, model_output=y)\n            outputs.append(output)\n\n        return outputs\n", "path": "runtimes/sklearn/mlserver_sklearn/sklearn.py"}, {"content": "DockerfileName = \"Dockerfile\"\nDockerfileTemplate = \"\"\"\nFROM continuumio/miniconda3:4.10.3 AS env-builder\nSHELL [\"/bin/bash\", \"-c\"]\n\nARG MLSERVER_ENV_NAME=\"mlserver-custom-env\" \\\\\n    MLSERVER_ENV_TARBALL=\"./envs/base.tar.gz\"\n\nRUN conda config --add channels conda-forge && \\\\\n    conda install conda-pack\n\n# The `[]` character range will ensure that Docker doesn't complain if the\n# files don't exist:\n# https://stackoverflow.com/a/65138098/5015573\nCOPY \\\\\n    ./environment.ym[l] \\\\\n    ./environment.yam[l] \\\\\n    ./conda.ym[l] \\\\\n    ./conda.yam[l] \\\\\n    .\nRUN mkdir $(dirname $MLSERVER_ENV_TARBALL); \\\\\n    for envFile in environment.yml environment.yaml conda.yml conda.yaml; do \\\\\n        if [[ -f $envFile ]]; then \\\\\n            conda env create \\\n                --name $MLSERVER_ENV_NAME \\\\\n                --file $envFile; \\\\\n            conda-pack \\\n                -n $MLSERVER_ENV_NAME \\\\\n                -o $MLSERVER_ENV_TARBALL; \\\\\n        fi \\\\\n    done; \\\\\n    chmod -R 776 $(dirname $MLSERVER_ENV_TARBALL)\n\nFROM seldonio/mlserver:{version}-slim\nSHELL [\"/bin/bash\", \"-c\"]\n\n# Copy all potential sources for custom environments\nCOPY \\\\\n    --chown=1000 \\\\\n    --from=env-builder \\\\\n    /envs/base.tar.g[z] \\\\\n    ./envs/base.tar.gz\nCOPY \\\\\n    ./settings.jso[n] \\\\\n    ./model-settings.jso[n] \\\\\n    ./requirements.tx[t] \\\\\n    .\n\nUSER root\n# Install dependencies system-wide, to ensure that they are available for every\n# user\nRUN ./hack/build-env.sh . ./envs/base && \\\n    chown -R 1000:0 ./envs/base && \\\\\n    chmod -R 776 ./envs/base\nUSER 1000\n\n# Copy everything else\nCOPY . .\n\n# Override MLServer's own `CMD` to activate the embedded environment\n# (optionally activating the hot-loaded one as well).\nCMD source ./hack/activate-env.sh ./envs/base.tar.gz ./envs/base && \\\\\n    mlserver start $MLSERVER_MODELS_DIR\n\"\"\"\n\nDockerignoreName = \".dockerignore\"\nDockerignore = \"\"\"\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n*.pyc\n*.pyo\n*.pyd\nbin\n\n# Mac file system\n**/.DS_Store\n\n# Python dev\n__pycache__\n.Python\nenv\npip-log.txt\npip-delete-this-directory.txt\n.mypy_cache\neggs/\n.eggs/\n*.egg-info/\n./pytest_cache\n.tox\nbuild/\ndist/\n\n# Notebook Checkpoints\n.ipynb_checkpoints\n\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n*.log\n.git\n\"\"\"\n", "path": "mlserver/cli/constants.py"}]}