We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Make the model's URI relative to the model-settings.json file
Currently, the model URI is expected to be relative to where the `mlserver` command is running from. However, this can be unintuitive for users, who may expect that path to be relative to where it's specified, i.e. the `model-settings.json` file.

It would be good to consider whether changing the reference for the path could make this more intuitive for users and what could its side-effects.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mlserver/repository.py`
Content:
```
1 import os
2 import glob
3 
4 from typing import List
5 
6 from .settings import ModelParameters, ModelSettings
7 from .errors import ModelNotFound
8 from .logging import logger
9 
10 DEFAULT_MODEL_SETTINGS_FILENAME = "model-settings.json"
11 
12 
13 class ModelRepository:
14     """
15     Model repository, responsible of the discovery of models which can be
16     loaded onto the model registry.
17     """
18 
19     def __init__(self, root: str = None):
20         self._root = root
21 
22     async def list(self) -> List[ModelSettings]:
23         all_model_settings = []
24 
25         # TODO: Use an async alternative for filesys ops
26         if self._root:
27             pattern = os.path.join(self._root, "**", DEFAULT_MODEL_SETTINGS_FILENAME)
28             matches = glob.glob(pattern, recursive=True)
29 
30             for model_settings_path in matches:
31                 model_settings = self._load_model_settings(model_settings_path)
32                 all_model_settings.append(model_settings)
33 
34         # If there were no matches, try to load model from environment
35         if not all_model_settings:
36             # return default
37             model_settings = ModelSettings()
38             model_settings.parameters = ModelParameters()
39             all_model_settings.append(model_settings)
40 
41         return all_model_settings
42 
43     def _load_model_settings(self, model_settings_path: str) -> ModelSettings:
44         model_settings = ModelSettings.parse_file(model_settings_path)
45 
46         # If name not present, default to folder name
47         default_model_name = os.path.basename(os.path.dirname(model_settings_path))
48         if model_settings.name:
49             if model_settings.name != default_model_name:
50                 # Raise warning if name is different than folder's name
51                 logger.warning(
52                     f"Model name '{model_settings.name}' is different than "
53                     f"model's folder name '{default_model_name}'."
54                 )
55         else:
56             model_settings.name = default_model_name
57 
58         if not model_settings.parameters:
59             model_settings.parameters = ModelParameters()
60 
61         if not model_settings.parameters.uri:
62             # If not specified, default to its own folder
63             default_model_uri = os.path.dirname(model_settings_path)
64             model_settings.parameters.uri = default_model_uri
65 
66         return model_settings
67 
68     async def find(self, name: str) -> ModelSettings:
69         all_settings = await self.list()
70         for model_settings in all_settings:
71             if model_settings.name == name:
72                 # TODO: Implement version policy
73                 return model_settings
74 
75         raise ModelNotFound(name)
76
```

Path: `mlserver/utils.py`
Content:
```
1 import os
2 import uuid
3 
4 from typing import Callable, List
5 
6 from .settings import ModelSettings
7 from .errors import InvalidModelURI
8 
9 
10 async def get_model_uri(
11     settings: ModelSettings, wellknown_filenames: List[str] = []
12 ) -> str:
13     if not settings.parameters:
14         raise InvalidModelURI(settings.name)
15 
16     model_uri = settings.parameters.uri
17 
18     if not model_uri:
19         raise InvalidModelURI(settings.name)
20 
21     if os.path.isfile(model_uri):
22         return model_uri
23 
24     if os.path.isdir(model_uri):
25         # If model_uri is a folder, search for a well-known model filename
26         for fname in wellknown_filenames:
27             model_path = os.path.join(model_uri, fname)
28             if os.path.isfile(model_path):
29                 return model_path
30 
31         # If none, return the folder
32         return model_uri
33 
34     # Otherwise, the uri is neither a file nor a folder
35     raise InvalidModelURI(settings.name, model_uri)
36 
37 
38 def get_wrapped_method(f: Callable) -> Callable:
39     while hasattr(f, "__wrapped__"):
40         f = f.__wrapped__  # type: ignore
41 
42     return f
43 
44 
45 def generate_uuid() -> str:
46     return str(uuid.uuid4())
47
```

Path: `mlserver/settings.py`
Content:
```
1 from typing import List, Optional
2 from pydantic import BaseSettings, PyObject
3 
4 from .version import __version__
5 from .types import MetadataTensor
6 
7 ENV_PREFIX_SETTINGS = "MLSERVER_"
8 ENV_PREFIX_MODEL_SETTINGS = "MLSERVER_MODEL_"
9 
10 
11 class Settings(BaseSettings):
12     class Config:
13         env_prefix = ENV_PREFIX_SETTINGS
14 
15     debug: bool = True
16 
17     # Model repository settings
18     """Root of the model repository, where we will search for models."""
19     model_repository_root: str = "."
20     """Flag to load all available models automatically at startup."""
21     load_models_at_startup: bool = True
22 
23     # Server metadata
24     """Name of the server."""
25     server_name: str = "mlserver"
26     """Version of the server."""
27     server_version: str = __version__
28     """Server extensions loaded."""
29     extensions: List[str] = []
30 
31     # Server settings
32     """Host where to listen for connections."""
33     host: str = "0.0.0.0"
34     """Port where to listen for HTTP / REST connections."""
35     http_port: int = 8080
36     """Port where to listen for gRPC connections."""
37     grpc_port: int = 8081
38     """Maximum length (i.e. size) of gRPC payloads."""
39     grpc_max_message_length: Optional[int] = None
40 
41 
42 class ModelParameters(BaseSettings):
43     """
44     Parameters that apply only to a particular instance of a model.
45     This can include things like model weights, or arbitrary ``extra``
46     parameters particular to the underlying inference runtime.
47     The main difference with respect to ``ModelSettings`` is that parameters
48     can change on each instance (e.g. each version) of the model.
49     """
50 
51     class Config:
52         env_prefix = ENV_PREFIX_MODEL_SETTINGS
53 
54     """
55     URI where the model artifacts can be found.
56     This path must be either absolute or relative to where MLServer is running.
57     """
58     uri: Optional[str] = None
59     """Version of the model."""
60     version: Optional[str] = None
61     """Format of the model (only available on certain runtimes)."""
62     format: Optional[str] = None
63     """Default content type to use for requests and responses."""
64     content_type: Optional[str] = None
65     """Arbitrary settings, dependent on the inference runtime
66     implementation."""
67     extra: Optional[dict] = {}
68 
69 
70 class ModelSettings(BaseSettings):
71     class Config:
72         env_prefix = ENV_PREFIX_MODEL_SETTINGS
73 
74     """Name of the model."""
75     name: str = ""
76 
77     # Model metadata
78     """Framework used to train and serialise the model (e.g. sklearn)."""
79     platform: str = ""
80     """Versions of dependencies used to train the model (e.g.
81     sklearn/0.20.1)."""
82     versions: List[str] = []
83     """Metadata about the inputs accepted by the model."""
84     inputs: List[MetadataTensor] = []
85     """Metadata about the outputs returned by the model."""
86     outputs: List[MetadataTensor] = []
87 
88     # Parallel settings
89     """When parallel inference is enabled, number of workers to run inference
90     across."""
91     parallel_workers: int = 4
92 
93     # Adaptive Batching settings (disabled by default)
94     """When adaptive batching is enabled, maximum number of requests to group
95     together in a single batch."""
96     max_batch_size: int = 0
97     """When adaptive batching is enabled, maximum amount of time (in seconds)
98     to wait for enough requests to build a full batch."""
99     max_batch_time: float = 0.0
100 
101     # Custom model class implementation
102     """*Python path* to the inference runtime to use to serve this model (e.g.
103     ``mlserver_sklearn.SKLearnModel``)."""
104     implementation: PyObject = "mlserver.model.MLModel"  # type: ignore
105 
106     # Model parameters are meant to be set directly by the MLServer runtime.
107     # However, it's also possible to override them manually.
108     """Extra parameters for each instance of this model."""
109     parameters: Optional[ModelParameters] = None
110
```
--- 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/repository.py b/mlserver/repository.py
--- a/mlserver/repository.py
+++ b/mlserver/repository.py
@@ -42,9 +42,11 @@
 
     def _load_model_settings(self, model_settings_path: str) -> ModelSettings:
         model_settings = ModelSettings.parse_file(model_settings_path)
+        model_settings._source = model_settings_path
 
         # If name not present, default to folder name
-        default_model_name = os.path.basename(os.path.dirname(model_settings_path))
+        model_settings_folder = os.path.dirname(model_settings_path)
+        default_model_name = os.path.basename(model_settings_folder)
         if model_settings.name:
             if model_settings.name != default_model_name:
                 # Raise warning if name is different than folder's name
diff --git a/mlserver/settings.py b/mlserver/settings.py
--- a/mlserver/settings.py
+++ b/mlserver/settings.py
@@ -70,6 +70,10 @@
 class ModelSettings(BaseSettings):
     class Config:
         env_prefix = ENV_PREFIX_MODEL_SETTINGS
+        underscore_attrs_are_private = True
+
+    # Source points to the file where model settings were loaded from
+    _source: Optional[str] = None
 
     """Name of the model."""
     name: str = ""
diff --git a/mlserver/utils.py b/mlserver/utils.py
--- a/mlserver/utils.py
+++ b/mlserver/utils.py
@@ -1,7 +1,7 @@
 import os
 import uuid
 
-from typing import Callable, List
+from typing import Callable, Optional, List
 
 from .settings import ModelSettings
 from .errors import InvalidModelURI
@@ -14,25 +14,36 @@
         raise InvalidModelURI(settings.name)
 
     model_uri = settings.parameters.uri
-
     if not model_uri:
         raise InvalidModelURI(settings.name)
 
-    if os.path.isfile(model_uri):
-        return model_uri
+    full_model_uri = _to_absolute_path(settings._source, model_uri)
+    if os.path.isfile(full_model_uri):
+        return full_model_uri
 
-    if os.path.isdir(model_uri):
-        # If model_uri is a folder, search for a well-known model filename
+    if os.path.isdir(full_model_uri):
+        # If full_model_uri is a folder, search for a well-known model filename
         for fname in wellknown_filenames:
-            model_path = os.path.join(model_uri, fname)
+            model_path = os.path.join(full_model_uri, fname)
             if os.path.isfile(model_path):
                 return model_path
 
         # If none, return the folder
-        return model_uri
+        return full_model_uri
 
     # Otherwise, the uri is neither a file nor a folder
-    raise InvalidModelURI(settings.name, model_uri)
+    raise InvalidModelURI(settings.name, full_model_uri)
+
+
+def _to_absolute_path(source: Optional[str], model_uri: str) -> str:
+    if source is None:
+        # Treat path as either absolute or relative to the working directory of
+        # the MLServer instance
+        return model_uri
+
+    parent_folder = os.path.dirname(source)
+    unnormalised = os.path.join(parent_folder, model_uri)
+    return os.path.normpath(unnormalised)
 
 
 def get_wrapped_method(f: Callable) -> Callable:

{"golden_diff": "diff --git a/mlserver/repository.py b/mlserver/repository.py\n--- a/mlserver/repository.py\n+++ b/mlserver/repository.py\n@@ -42,9 +42,11 @@\n \n     def _load_model_settings(self, model_settings_path: str) -> ModelSettings:\n         model_settings = ModelSettings.parse_file(model_settings_path)\n+        model_settings._source = model_settings_path\n \n         # If name not present, default to folder name\n-        default_model_name = os.path.basename(os.path.dirname(model_settings_path))\n+        model_settings_folder = os.path.dirname(model_settings_path)\n+        default_model_name = os.path.basename(model_settings_folder)\n         if model_settings.name:\n             if model_settings.name != default_model_name:\n                 # Raise warning if name is different than folder's name\ndiff --git a/mlserver/settings.py b/mlserver/settings.py\n--- a/mlserver/settings.py\n+++ b/mlserver/settings.py\n@@ -70,6 +70,10 @@\n class ModelSettings(BaseSettings):\n     class Config:\n         env_prefix = ENV_PREFIX_MODEL_SETTINGS\n+        underscore_attrs_are_private = True\n+\n+    # Source points to the file where model settings were loaded from\n+    _source: Optional[str] = None\n \n     \"\"\"Name of the model.\"\"\"\n     name: str = \"\"\ndiff --git a/mlserver/utils.py b/mlserver/utils.py\n--- a/mlserver/utils.py\n+++ b/mlserver/utils.py\n@@ -1,7 +1,7 @@\n import os\n import uuid\n \n-from typing import Callable, List\n+from typing import Callable, Optional, List\n \n from .settings import ModelSettings\n from .errors import InvalidModelURI\n@@ -14,25 +14,36 @@\n         raise InvalidModelURI(settings.name)\n \n     model_uri = settings.parameters.uri\n-\n     if not model_uri:\n         raise InvalidModelURI(settings.name)\n \n-    if os.path.isfile(model_uri):\n-        return model_uri\n+    full_model_uri = _to_absolute_path(settings._source, model_uri)\n+    if os.path.isfile(full_model_uri):\n+        return full_model_uri\n \n-    if os.path.isdir(model_uri):\n-        # If model_uri is a folder, search for a well-known model filename\n+    if os.path.isdir(full_model_uri):\n+        # If full_model_uri is a folder, search for a well-known model filename\n         for fname in wellknown_filenames:\n-            model_path = os.path.join(model_uri, fname)\n+            model_path = os.path.join(full_model_uri, fname)\n             if os.path.isfile(model_path):\n                 return model_path\n \n         # If none, return the folder\n-        return model_uri\n+        return full_model_uri\n \n     # Otherwise, the uri is neither a file nor a folder\n-    raise InvalidModelURI(settings.name, model_uri)\n+    raise InvalidModelURI(settings.name, full_model_uri)\n+\n+\n+def _to_absolute_path(source: Optional[str], model_uri: str) -> str:\n+    if source is None:\n+        # Treat path as either absolute or relative to the working directory of\n+        # the MLServer instance\n+        return model_uri\n+\n+    parent_folder = os.path.dirname(source)\n+    unnormalised = os.path.join(parent_folder, model_uri)\n+    return os.path.normpath(unnormalised)\n \n \n def get_wrapped_method(f: Callable) -> Callable:\n", "issue": "Make the model's URI relative to the model-settings.json file\nCurrently, the model URI is expected to be relative to where the `mlserver` command is running from. However, this can be unintuitive for users, who may expect that path to be relative to where it's specified, i.e. the `model-settings.json` file.\n\nIt would be good to consider whether changing the reference for the path could make this more intuitive for users and what could its side-effects.\n", "before_files": [{"content": "import os\nimport glob\n\nfrom typing import List\n\nfrom .settings import ModelParameters, ModelSettings\nfrom .errors import ModelNotFound\nfrom .logging import logger\n\nDEFAULT_MODEL_SETTINGS_FILENAME = \"model-settings.json\"\n\n\nclass ModelRepository:\n    \"\"\"\n    Model repository, responsible of the discovery of models which can be\n    loaded onto the model registry.\n    \"\"\"\n\n    def __init__(self, root: str = None):\n        self._root = root\n\n    async def list(self) -> List[ModelSettings]:\n        all_model_settings = []\n\n        # TODO: Use an async alternative for filesys ops\n        if self._root:\n            pattern = os.path.join(self._root, \"**\", DEFAULT_MODEL_SETTINGS_FILENAME)\n            matches = glob.glob(pattern, recursive=True)\n\n            for model_settings_path in matches:\n                model_settings = self._load_model_settings(model_settings_path)\n                all_model_settings.append(model_settings)\n\n        # If there were no matches, try to load model from environment\n        if not all_model_settings:\n            # return default\n            model_settings = ModelSettings()\n            model_settings.parameters = ModelParameters()\n            all_model_settings.append(model_settings)\n\n        return all_model_settings\n\n    def _load_model_settings(self, model_settings_path: str) -> ModelSettings:\n        model_settings = ModelSettings.parse_file(model_settings_path)\n\n        # If name not present, default to folder name\n        default_model_name = os.path.basename(os.path.dirname(model_settings_path))\n        if model_settings.name:\n            if model_settings.name != default_model_name:\n                # Raise warning if name is different than folder's name\n                logger.warning(\n                    f\"Model name '{model_settings.name}' is different than \"\n                    f\"model's folder name '{default_model_name}'.\"\n                )\n        else:\n            model_settings.name = default_model_name\n\n        if not model_settings.parameters:\n            model_settings.parameters = ModelParameters()\n\n        if not model_settings.parameters.uri:\n            # If not specified, default to its own folder\n            default_model_uri = os.path.dirname(model_settings_path)\n            model_settings.parameters.uri = default_model_uri\n\n        return model_settings\n\n    async def find(self, name: str) -> ModelSettings:\n        all_settings = await self.list()\n        for model_settings in all_settings:\n            if model_settings.name == name:\n                # TODO: Implement version policy\n                return model_settings\n\n        raise ModelNotFound(name)\n", "path": "mlserver/repository.py"}, {"content": "import os\nimport uuid\n\nfrom typing import Callable, List\n\nfrom .settings import ModelSettings\nfrom .errors import InvalidModelURI\n\n\nasync def get_model_uri(\n    settings: ModelSettings, wellknown_filenames: List[str] = []\n) -> str:\n    if not settings.parameters:\n        raise InvalidModelURI(settings.name)\n\n    model_uri = settings.parameters.uri\n\n    if not model_uri:\n        raise InvalidModelURI(settings.name)\n\n    if os.path.isfile(model_uri):\n        return model_uri\n\n    if os.path.isdir(model_uri):\n        # If model_uri is a folder, search for a well-known model filename\n        for fname in wellknown_filenames:\n            model_path = os.path.join(model_uri, fname)\n            if os.path.isfile(model_path):\n                return model_path\n\n        # If none, return the folder\n        return model_uri\n\n    # Otherwise, the uri is neither a file nor a folder\n    raise InvalidModelURI(settings.name, model_uri)\n\n\ndef get_wrapped_method(f: Callable) -> Callable:\n    while hasattr(f, \"__wrapped__\"):\n        f = f.__wrapped__  # type: ignore\n\n    return f\n\n\ndef generate_uuid() -> str:\n    return str(uuid.uuid4())\n", "path": "mlserver/utils.py"}, {"content": "from typing import List, Optional\nfrom pydantic import BaseSettings, PyObject\n\nfrom .version import __version__\nfrom .types import MetadataTensor\n\nENV_PREFIX_SETTINGS = \"MLSERVER_\"\nENV_PREFIX_MODEL_SETTINGS = \"MLSERVER_MODEL_\"\n\n\nclass Settings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    debug: bool = True\n\n    # Model repository settings\n    \"\"\"Root of the model repository, where we will search for models.\"\"\"\n    model_repository_root: str = \".\"\n    \"\"\"Flag to load all available models automatically at startup.\"\"\"\n    load_models_at_startup: bool = True\n\n    # Server metadata\n    \"\"\"Name of the server.\"\"\"\n    server_name: str = \"mlserver\"\n    \"\"\"Version of the server.\"\"\"\n    server_version: str = __version__\n    \"\"\"Server extensions loaded.\"\"\"\n    extensions: List[str] = []\n\n    # Server settings\n    \"\"\"Host where to listen for connections.\"\"\"\n    host: str = \"0.0.0.0\"\n    \"\"\"Port where to listen for HTTP / REST connections.\"\"\"\n    http_port: int = 8080\n    \"\"\"Port where to listen for gRPC connections.\"\"\"\n    grpc_port: int = 8081\n    \"\"\"Maximum length (i.e. size) of gRPC payloads.\"\"\"\n    grpc_max_message_length: Optional[int] = None\n\n\nclass ModelParameters(BaseSettings):\n    \"\"\"\n    Parameters that apply only to a particular instance of a model.\n    This can include things like model weights, or arbitrary ``extra``\n    parameters particular to the underlying inference runtime.\n    The main difference with respect to ``ModelSettings`` is that parameters\n    can change on each instance (e.g. each version) of the model.\n    \"\"\"\n\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    \"\"\"\n    URI where the model artifacts can be found.\n    This path must be either absolute or relative to where MLServer is running.\n    \"\"\"\n    uri: Optional[str] = None\n    \"\"\"Version of the model.\"\"\"\n    version: Optional[str] = None\n    \"\"\"Format of the model (only available on certain runtimes).\"\"\"\n    format: Optional[str] = None\n    \"\"\"Default content type to use for requests and responses.\"\"\"\n    content_type: Optional[str] = None\n    \"\"\"Arbitrary settings, dependent on the inference runtime\n    implementation.\"\"\"\n    extra: Optional[dict] = {}\n\n\nclass ModelSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    \"\"\"Name of the model.\"\"\"\n    name: str = \"\"\n\n    # Model metadata\n    \"\"\"Framework used to train and serialise the model (e.g. sklearn).\"\"\"\n    platform: str = \"\"\n    \"\"\"Versions of dependencies used to train the model (e.g.\n    sklearn/0.20.1).\"\"\"\n    versions: List[str] = []\n    \"\"\"Metadata about the inputs accepted by the model.\"\"\"\n    inputs: List[MetadataTensor] = []\n    \"\"\"Metadata about the outputs returned by the model.\"\"\"\n    outputs: List[MetadataTensor] = []\n\n    # Parallel settings\n    \"\"\"When parallel inference is enabled, number of workers to run inference\n    across.\"\"\"\n    parallel_workers: int = 4\n\n    # Adaptive Batching settings (disabled by default)\n    \"\"\"When adaptive batching is enabled, maximum number of requests to group\n    together in a single batch.\"\"\"\n    max_batch_size: int = 0\n    \"\"\"When adaptive batching is enabled, maximum amount of time (in seconds)\n    to wait for enough requests to build a full batch.\"\"\"\n    max_batch_time: float = 0.0\n\n    # Custom model class implementation\n    \"\"\"*Python path* to the inference runtime to use to serve this model (e.g.\n    ``mlserver_sklearn.SKLearnModel``).\"\"\"\n    implementation: PyObject = \"mlserver.model.MLModel\"  # type: ignore\n\n    # Model parameters are meant to be set directly by the MLServer runtime.\n    # However, it's also possible to override them manually.\n    \"\"\"Extra parameters for each instance of this model.\"\"\"\n    parameters: Optional[ModelParameters] = None\n", "path": "mlserver/settings.py"}], "after_files": [{"content": "import os\nimport glob\n\nfrom typing import List\n\nfrom .settings import ModelParameters, ModelSettings\nfrom .errors import ModelNotFound\nfrom .logging import logger\n\nDEFAULT_MODEL_SETTINGS_FILENAME = \"model-settings.json\"\n\n\nclass ModelRepository:\n    \"\"\"\n    Model repository, responsible of the discovery of models which can be\n    loaded onto the model registry.\n    \"\"\"\n\n    def __init__(self, root: str = None):\n        self._root = root\n\n    async def list(self) -> List[ModelSettings]:\n        all_model_settings = []\n\n        # TODO: Use an async alternative for filesys ops\n        if self._root:\n            pattern = os.path.join(self._root, \"**\", DEFAULT_MODEL_SETTINGS_FILENAME)\n            matches = glob.glob(pattern, recursive=True)\n\n            for model_settings_path in matches:\n                model_settings = self._load_model_settings(model_settings_path)\n                all_model_settings.append(model_settings)\n\n        # If there were no matches, try to load model from environment\n        if not all_model_settings:\n            # return default\n            model_settings = ModelSettings()\n            model_settings.parameters = ModelParameters()\n            all_model_settings.append(model_settings)\n\n        return all_model_settings\n\n    def _load_model_settings(self, model_settings_path: str) -> ModelSettings:\n        model_settings = ModelSettings.parse_file(model_settings_path)\n        model_settings._source = model_settings_path\n\n        # If name not present, default to folder name\n        model_settings_folder = os.path.dirname(model_settings_path)\n        default_model_name = os.path.basename(model_settings_folder)\n        if model_settings.name:\n            if model_settings.name != default_model_name:\n                # Raise warning if name is different than folder's name\n                logger.warning(\n                    f\"Model name '{model_settings.name}' is different than \"\n                    f\"model's folder name '{default_model_name}'.\"\n                )\n        else:\n            model_settings.name = default_model_name\n\n        if not model_settings.parameters:\n            model_settings.parameters = ModelParameters()\n\n        if not model_settings.parameters.uri:\n            # If not specified, default to its own folder\n            default_model_uri = os.path.dirname(model_settings_path)\n            model_settings.parameters.uri = default_model_uri\n\n        return model_settings\n\n    async def find(self, name: str) -> ModelSettings:\n        all_settings = await self.list()\n        for model_settings in all_settings:\n            if model_settings.name == name:\n                # TODO: Implement version policy\n                return model_settings\n\n        raise ModelNotFound(name)\n", "path": "mlserver/repository.py"}, {"content": "import os\nimport uuid\n\nfrom typing import Callable, Optional, List\n\nfrom .settings import ModelSettings\nfrom .errors import InvalidModelURI\n\n\nasync def get_model_uri(\n    settings: ModelSettings, wellknown_filenames: List[str] = []\n) -> str:\n    if not settings.parameters:\n        raise InvalidModelURI(settings.name)\n\n    model_uri = settings.parameters.uri\n    if not model_uri:\n        raise InvalidModelURI(settings.name)\n\n    full_model_uri = _to_absolute_path(settings._source, model_uri)\n    if os.path.isfile(full_model_uri):\n        return full_model_uri\n\n    if os.path.isdir(full_model_uri):\n        # If full_model_uri is a folder, search for a well-known model filename\n        for fname in wellknown_filenames:\n            model_path = os.path.join(full_model_uri, fname)\n            if os.path.isfile(model_path):\n                return model_path\n\n        # If none, return the folder\n        return full_model_uri\n\n    # Otherwise, the uri is neither a file nor a folder\n    raise InvalidModelURI(settings.name, full_model_uri)\n\n\ndef _to_absolute_path(source: Optional[str], model_uri: str) -> str:\n    if source is None:\n        # Treat path as either absolute or relative to the working directory of\n        # the MLServer instance\n        return model_uri\n\n    parent_folder = os.path.dirname(source)\n    unnormalised = os.path.join(parent_folder, model_uri)\n    return os.path.normpath(unnormalised)\n\n\ndef get_wrapped_method(f: Callable) -> Callable:\n    while hasattr(f, \"__wrapped__\"):\n        f = f.__wrapped__  # type: ignore\n\n    return f\n\n\ndef generate_uuid() -> str:\n    return str(uuid.uuid4())\n", "path": "mlserver/utils.py"}, {"content": "from typing import List, Optional\nfrom pydantic import BaseSettings, PyObject\n\nfrom .version import __version__\nfrom .types import MetadataTensor\n\nENV_PREFIX_SETTINGS = \"MLSERVER_\"\nENV_PREFIX_MODEL_SETTINGS = \"MLSERVER_MODEL_\"\n\n\nclass Settings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    debug: bool = True\n\n    # Model repository settings\n    \"\"\"Root of the model repository, where we will search for models.\"\"\"\n    model_repository_root: str = \".\"\n    \"\"\"Flag to load all available models automatically at startup.\"\"\"\n    load_models_at_startup: bool = True\n\n    # Server metadata\n    \"\"\"Name of the server.\"\"\"\n    server_name: str = \"mlserver\"\n    \"\"\"Version of the server.\"\"\"\n    server_version: str = __version__\n    \"\"\"Server extensions loaded.\"\"\"\n    extensions: List[str] = []\n\n    # Server settings\n    \"\"\"Host where to listen for connections.\"\"\"\n    host: str = \"0.0.0.0\"\n    \"\"\"Port where to listen for HTTP / REST connections.\"\"\"\n    http_port: int = 8080\n    \"\"\"Port where to listen for gRPC connections.\"\"\"\n    grpc_port: int = 8081\n    \"\"\"Maximum length (i.e. size) of gRPC payloads.\"\"\"\n    grpc_max_message_length: Optional[int] = None\n\n\nclass ModelParameters(BaseSettings):\n    \"\"\"\n    Parameters that apply only to a particular instance of a model.\n    This can include things like model weights, or arbitrary ``extra``\n    parameters particular to the underlying inference runtime.\n    The main difference with respect to ``ModelSettings`` is that parameters\n    can change on each instance (e.g. each version) of the model.\n    \"\"\"\n\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    \"\"\"\n    URI where the model artifacts can be found.\n    This path must be either absolute or relative to where MLServer is running.\n    \"\"\"\n    uri: Optional[str] = None\n    \"\"\"Version of the model.\"\"\"\n    version: Optional[str] = None\n    \"\"\"Format of the model (only available on certain runtimes).\"\"\"\n    format: Optional[str] = None\n    \"\"\"Default content type to use for requests and responses.\"\"\"\n    content_type: Optional[str] = None\n    \"\"\"Arbitrary settings, dependent on the inference runtime\n    implementation.\"\"\"\n    extra: Optional[dict] = {}\n\n\nclass ModelSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n        underscore_attrs_are_private = True\n\n    # Source points to the file where model settings were loaded from\n    _source: Optional[str] = None\n\n    \"\"\"Name of the model.\"\"\"\n    name: str = \"\"\n\n    # Model metadata\n    \"\"\"Framework used to train and serialise the model (e.g. sklearn).\"\"\"\n    platform: str = \"\"\n    \"\"\"Versions of dependencies used to train the model (e.g.\n    sklearn/0.20.1).\"\"\"\n    versions: List[str] = []\n    \"\"\"Metadata about the inputs accepted by the model.\"\"\"\n    inputs: List[MetadataTensor] = []\n    \"\"\"Metadata about the outputs returned by the model.\"\"\"\n    outputs: List[MetadataTensor] = []\n\n    # Parallel settings\n    \"\"\"When parallel inference is enabled, number of workers to run inference\n    across.\"\"\"\n    parallel_workers: int = 4\n\n    # Adaptive Batching settings (disabled by default)\n    \"\"\"When adaptive batching is enabled, maximum number of requests to group\n    together in a single batch.\"\"\"\n    max_batch_size: int = 0\n    \"\"\"When adaptive batching is enabled, maximum amount of time (in seconds)\n    to wait for enough requests to build a full batch.\"\"\"\n    max_batch_time: float = 0.0\n\n    # Custom model class implementation\n    \"\"\"*Python path* to the inference runtime to use to serve this model (e.g.\n    ``mlserver_sklearn.SKLearnModel``).\"\"\"\n    implementation: PyObject = \"mlserver.model.MLModel\"  # type: ignore\n\n    # Model parameters are meant to be set directly by the MLServer runtime.\n    # However, it's also possible to override them manually.\n    \"\"\"Extra parameters for each instance of this model.\"\"\"\n    parameters: Optional[ModelParameters] = None\n", "path": "mlserver/settings.py"}]}