We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug: OpenAPI Schema components from mounted ASGI apps are not being included 
### Describe the bug

If a mounted ASGI app has an OpenAPI spec that defines schema components, these are not included in the bento's generated OpenAPI spec.

The following service file reproduces the issue:

```python
import bentoml
import pydantic
from fastapi import FastAPI

svc = bentoml.Service(name="test", runners=[])

fastapi_app = FastAPI()
svc.mount_asgi_app(fastapi_app)


class TestSchema(pydantic.BaseModel):
    text_field: str


@fastapi_app.get("/metadata")
def metadata() -> TestSchema:
    return TestSchema(text_field="Hello world")

```

If I serve this bento and navigate to the OpenAPI docs, the following error is raised: 
```
Could not resolve reference: Could not resolve pointer: /components/schemas/TestSchema does not exist in document
```

This is happening because the OpenAPI path components are being pulled in from the mounted app, but the schema component (the TestSchema class in this case) are not being pulled in. I've got a fix ready for this and will open a PR shortly

### To reproduce

Requires fastapi and pydantic:
`pip install fastapi pydantic`

This service file reproduces the issue:
```python
import bentoml
import pydantic
from fastapi import FastAPI

svc = bentoml.Service(name="test", runners=[])

fastapi_app = FastAPI()
svc.mount_asgi_app(fastapi_app)


class TestSchema(pydantic.BaseModel):
    text_field: str


@fastapi_app.get("/metadata")
def metadata() -> TestSchema:
    return TestSchema(text_field="Hello world")

```

`bentoml serve service.py:svc`

### Expected behavior

The FastAPI app's schema definitions should be included in the generated OpenAPI spec.

### Environment

#### Environment variable

```bash
BENTOML_DEBUG=''
BENTOML_QUIET=''
BENTOML_BUNDLE_LOCAL_BUILD=''
BENTOML_DO_NOT_TRACK=''
BENTOML_CONFIG=''
BENTOML_CONFIG_OPTIONS=''
BENTOML_PORT=''
BENTOML_HOST=''
BENTOML_API_WORKERS=''
```

#### System information

`bentoml`: 1.1.6
`python`: 3.10.12
`platform`: Linux-6.2.0-33-generic-x86_64-with-glibc2.35
`uid_gid`: 1000:1000
<details><summary><code>pip_packages</code></summary>

<br>

```
aiohttp==3.8.5
aiosignal==1.3.1
annotated-types==0.5.0
anyio==3.7.1
appdirs==1.4.4
asgiref==3.7.2
async-timeout==4.0.3
attrs==23.1.0
bentoml==1.1.6
build==1.0.3
cattrs==23.1.2
certifi==2023.7.22
charset-normalizer==3.2.0
circus==0.18.0
click==8.1.7
click-option-group==0.5.6
cloudpickle==2.2.1
contextlib2==21.6.0
deepmerge==1.1.0
Deprecated==1.2.14
exceptiongroup==1.1.3
fastapi==0.103.1
frozenlist==1.4.0
fs==2.4.16
h11==0.14.0
idna==3.4
importlib-metadata==6.0.1
inflection==0.5.1
Jinja2==3.1.2
jsonschema==4.19.1
jsonschema-specifications==2023.7.1
markdown-it-py==3.0.0
MarkupSafe==2.1.3
mdurl==0.1.2
multidict==6.0.4
numpy==1.26.0
openapi==1.1.0
opentelemetry-api==1.18.0
opentelemetry-instrumentation==0.39b0
opentelemetry-instrumentation-aiohttp-client==0.39b0
opentelemetry-instrumentation-asgi==0.39b0
opentelemetry-sdk==1.18.0
opentelemetry-semantic-conventions==0.39b0
opentelemetry-util-http==0.39b0
packaging==23.1
pathspec==0.11.2
pip-requirements-parser==32.0.1
pip-tools==7.3.0
prometheus-client==0.17.1
psutil==5.9.5
pydantic==2.4.1
pydantic_core==2.10.1
Pygments==2.16.1
pynvml==11.5.0
pyparsing==3.1.1
pyproject_hooks==1.0.0
python-dateutil==2.8.2
python-json-logger==2.0.7
python-multipart==0.0.6
PyYAML==6.0.1
pyzmq==25.1.1
referencing==0.30.2
requests==2.31.0
rich==13.5.3
rpds-py==0.10.3
schema==0.7.5
simple-di==0.1.5
six==1.16.0
sniffio==1.3.0
starlette==0.27.0
tomli==2.0.1
tornado==6.3.3
typing_extensions==4.8.0
urllib3==2.0.5
uvicorn==0.23.2
watchfiles==0.20.0
wrapt==1.15.0
yarl==1.9.2
zipp==3.17.0
```

</details>



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/bentoml/_internal/service/openapi/__init__.py`
Content:
```
1 from __future__ import annotations
2 
3 import typing as t
4 from functools import lru_cache
5 from http import HTTPStatus
6 from typing import TYPE_CHECKING
7 
8 from deepmerge.merger import Merger
9 
10 from bentoml.exceptions import InternalServerError
11 from bentoml.exceptions import InvalidArgument
12 from bentoml.exceptions import NotFound
13 
14 from ...types import LazyType
15 from ...utils import bentoml_cattr
16 from .specification import Components
17 from .specification import Contact
18 from .specification import Info
19 from .specification import MediaType
20 from .specification import OpenAPISpecification
21 from .specification import Operation
22 from .specification import PathItem
23 from .specification import Reference
24 from .specification import Response
25 from .specification import Tag
26 from .utils import REF_PREFIX
27 from .utils import exception_components_schema
28 from .utils import exception_schema
29 
30 if TYPE_CHECKING:
31     from .. import Service
32     from ..inference_api import InferenceAPI
33 
34 SUCCESS_DESCRIPTION = "Successful Response"
35 
36 INFRA_DECRIPTION = {
37     "/healthz": "Health check endpoint. Expecting an empty response with status code <code>200</code> when the service is in health state. The <code>/healthz</code> endpoint is <b>deprecated</b>. (since Kubernetes v1.16)",
38     "/livez": "Health check endpoint for Kubernetes. Healthy endpoint responses with a <code>200</code> OK status.",
39     "/readyz": "A <code>200</code> OK status from <code>/readyz</code> endpoint indicated the service is ready to accept traffic. From that point and onward, Kubernetes will use <code>/livez</code> endpoint to perform periodic health checks.",
40     "/metrics": "Prometheus metrics endpoint. The <code>/metrics</code> responses with a <code>200</code>. The output can then be used by a Prometheus sidecar to scrape the metrics of the service.",
41 }
42 
43 __all__ = ["generate_spec"]
44 
45 INFRA_TAG = Tag(
46     name="Infrastructure",
47     description="Common infrastructure endpoints for observability.",
48 )
49 APP_TAG = Tag(
50     name="Service APIs", description="BentoML Service API endpoints for inference."
51 )
52 
53 merger = Merger(
54     # merge dicts
55     [(dict, "merge")],
56     # override all other types
57     ["override"],
58     # override conflicting types
59     ["override"],
60 )
61 
62 
63 def make_api_path(api: InferenceAPI[t.Any]) -> str:
64     return api.route if api.route.startswith("/") else f"/{api.route}"
65 
66 
67 @lru_cache(maxsize=1)
68 def make_infra_endpoints() -> dict[str, PathItem]:
69     return {
70         endpoint: PathItem(
71             get=Operation(
72                 responses={"200": Response(description=SUCCESS_DESCRIPTION)},
73                 tags=[INFRA_TAG.name],
74                 description=INFRA_DECRIPTION[endpoint],
75             )
76         )
77         for endpoint in INFRA_DECRIPTION
78     }
79 
80 
81 def generate_service_components(svc: Service) -> Components:
82     components: dict[str, t.Any] = {}
83     for api in svc.apis.values():
84         api_components = {}
85         input_components = api.input.openapi_components()
86         if input_components:
87             merger.merge(api_components, input_components)
88         output_components = api.output.openapi_components()
89         if output_components:
90             merger.merge(api_components, output_components)
91 
92         merger.merge(components, api_components)
93 
94     # merge exception at last
95     merger.merge(components, {"schemas": exception_components_schema()})
96 
97     return Components(**components)
98 
99 
100 def generate_spec(svc: Service, *, openapi_version: str = "3.0.2"):
101     """Generate a OpenAPI specification for a service."""
102     mounted_app_paths = {}
103 
104     for app, _, _ in svc.mount_apps:
105         if LazyType["fastapi.FastAPI"]("fastapi.FastAPI").isinstance(app):
106             from fastapi.openapi.utils import get_openapi
107 
108             openapi = get_openapi(
109                 title=app.title,
110                 version=app.version,
111                 routes=app.routes,
112             )
113 
114             mounted_app_paths.update(
115                 {
116                     k: bentoml_cattr.structure(v, PathItem)
117                     for k, v in openapi["paths"].items()
118                 }
119             )
120 
121     return OpenAPISpecification(
122         openapi=openapi_version,
123         tags=[APP_TAG, INFRA_TAG],
124         components=generate_service_components(svc),
125         info=Info(
126             title=svc.name,
127             description=svc.doc,
128             version=svc.tag.version if svc.tag and svc.tag.version else "None",
129             contact=Contact(name="BentoML Team", email="contact@bentoml.com"),
130         ),
131         servers=[{"url": "."}],
132         paths={
133             # setup infra endpoints
134             **make_infra_endpoints(),
135             # setup inference endpoints
136             **{
137                 make_api_path(api): PathItem(
138                     post={
139                         "responses": {
140                             HTTPStatus.OK.value: api.output.openapi_responses(),
141                             **{
142                                 ex.error_code.value: Response(
143                                     description=filled.description,
144                                     content={
145                                         "application/json": MediaType(
146                                             schema=Reference(
147                                                 f"{REF_PREFIX}{filled.title}"
148                                             )
149                                         )
150                                     },
151                                 )
152                                 for ex in [
153                                     InvalidArgument,
154                                     NotFound,
155                                     InternalServerError,
156                                 ]
157                                 for filled in exception_schema(ex)
158                             },
159                         },
160                         "tags": [APP_TAG.name],
161                         "consumes": [api.input.mime_type],
162                         "produces": [api.output.mime_type],
163                         "x-bentoml-name": api.name,
164                         "summary": str(api),
165                         "description": api.doc or "",
166                         "requestBody": api.input.openapi_request_body(),
167                         "operationId": f"{svc.name}__{api.name}",
168                     },
169                 )
170                 for api in svc.apis.values()
171             },
172             **mounted_app_paths,
173         },
174     )
175
```
--- 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/src/bentoml/_internal/service/openapi/__init__.py b/src/bentoml/_internal/service/openapi/__init__.py
--- a/src/bentoml/_internal/service/openapi/__init__.py
+++ b/src/bentoml/_internal/service/openapi/__init__.py
@@ -13,7 +13,6 @@
 
 from ...types import LazyType
 from ...utils import bentoml_cattr
-from .specification import Components
 from .specification import Contact
 from .specification import Info
 from .specification import MediaType
@@ -78,7 +77,7 @@
     }
 
 
-def generate_service_components(svc: Service) -> Components:
+def generate_service_components(svc: Service) -> dict[str, t.Any]:
     components: dict[str, t.Any] = {}
     for api in svc.apis.values():
         api_components = {}
@@ -92,14 +91,13 @@
         merger.merge(components, api_components)
 
     # merge exception at last
-    merger.merge(components, {"schemas": exception_components_schema()})
-
-    return Components(**components)
+    return merger.merge(components, {"schemas": exception_components_schema()})
 
 
 def generate_spec(svc: Service, *, openapi_version: str = "3.0.2"):
     """Generate a OpenAPI specification for a service."""
     mounted_app_paths = {}
+    schema_components = {}
 
     for app, _, _ in svc.mount_apps:
         if LazyType["fastapi.FastAPI"]("fastapi.FastAPI").isinstance(app):
@@ -118,10 +116,15 @@
                 }
             )
 
+            if "components" in openapi:
+                merger.merge(schema_components, openapi["components"])
+
+    merger.merge(schema_components, generate_service_components(svc))
+
     return OpenAPISpecification(
         openapi=openapi_version,
         tags=[APP_TAG, INFRA_TAG],
-        components=generate_service_components(svc),
+        components=schema_components,
         info=Info(
             title=svc.name,
             description=svc.doc,

{"golden_diff": "diff --git a/src/bentoml/_internal/service/openapi/__init__.py b/src/bentoml/_internal/service/openapi/__init__.py\n--- a/src/bentoml/_internal/service/openapi/__init__.py\n+++ b/src/bentoml/_internal/service/openapi/__init__.py\n@@ -13,7 +13,6 @@\n \n from ...types import LazyType\n from ...utils import bentoml_cattr\n-from .specification import Components\n from .specification import Contact\n from .specification import Info\n from .specification import MediaType\n@@ -78,7 +77,7 @@\n     }\n \n \n-def generate_service_components(svc: Service) -> Components:\n+def generate_service_components(svc: Service) -> dict[str, t.Any]:\n     components: dict[str, t.Any] = {}\n     for api in svc.apis.values():\n         api_components = {}\n@@ -92,14 +91,13 @@\n         merger.merge(components, api_components)\n \n     # merge exception at last\n-    merger.merge(components, {\"schemas\": exception_components_schema()})\n-\n-    return Components(**components)\n+    return merger.merge(components, {\"schemas\": exception_components_schema()})\n \n \n def generate_spec(svc: Service, *, openapi_version: str = \"3.0.2\"):\n     \"\"\"Generate a OpenAPI specification for a service.\"\"\"\n     mounted_app_paths = {}\n+    schema_components = {}\n \n     for app, _, _ in svc.mount_apps:\n         if LazyType[\"fastapi.FastAPI\"](\"fastapi.FastAPI\").isinstance(app):\n@@ -118,10 +116,15 @@\n                 }\n             )\n \n+            if \"components\" in openapi:\n+                merger.merge(schema_components, openapi[\"components\"])\n+\n+    merger.merge(schema_components, generate_service_components(svc))\n+\n     return OpenAPISpecification(\n         openapi=openapi_version,\n         tags=[APP_TAG, INFRA_TAG],\n-        components=generate_service_components(svc),\n+        components=schema_components,\n         info=Info(\n             title=svc.name,\n             description=svc.doc,\n", "issue": "bug: OpenAPI Schema components from mounted ASGI apps are not being included \n### Describe the bug\n\nIf a mounted ASGI app has an OpenAPI spec that defines schema components, these are not included in the bento's generated OpenAPI spec.\r\n\r\nThe following service file reproduces the issue:\r\n\r\n```python\r\nimport bentoml\r\nimport pydantic\r\nfrom fastapi import FastAPI\r\n\r\nsvc = bentoml.Service(name=\"test\", runners=[])\r\n\r\nfastapi_app = FastAPI()\r\nsvc.mount_asgi_app(fastapi_app)\r\n\r\n\r\nclass TestSchema(pydantic.BaseModel):\r\n    text_field: str\r\n\r\n\r\n@fastapi_app.get(\"/metadata\")\r\ndef metadata() -> TestSchema:\r\n    return TestSchema(text_field=\"Hello world\")\r\n\r\n```\r\n\r\nIf I serve this bento and navigate to the OpenAPI docs, the following error is raised: \r\n```\r\nCould not resolve reference: Could not resolve pointer: /components/schemas/TestSchema does not exist in document\r\n```\r\n\r\nThis is happening because the OpenAPI path components are being pulled in from the mounted app, but the schema component (the TestSchema class in this case) are not being pulled in. I've got a fix ready for this and will open a PR shortly\n\n### To reproduce\n\nRequires fastapi and pydantic:\r\n`pip install fastapi pydantic`\r\n\r\nThis service file reproduces the issue:\r\n```python\r\nimport bentoml\r\nimport pydantic\r\nfrom fastapi import FastAPI\r\n\r\nsvc = bentoml.Service(name=\"test\", runners=[])\r\n\r\nfastapi_app = FastAPI()\r\nsvc.mount_asgi_app(fastapi_app)\r\n\r\n\r\nclass TestSchema(pydantic.BaseModel):\r\n    text_field: str\r\n\r\n\r\n@fastapi_app.get(\"/metadata\")\r\ndef metadata() -> TestSchema:\r\n    return TestSchema(text_field=\"Hello world\")\r\n\r\n```\r\n\r\n`bentoml serve service.py:svc`\n\n### Expected behavior\n\nThe FastAPI app's schema definitions should be included in the generated OpenAPI spec.\n\n### Environment\n\n#### Environment variable\r\n\r\n```bash\r\nBENTOML_DEBUG=''\r\nBENTOML_QUIET=''\r\nBENTOML_BUNDLE_LOCAL_BUILD=''\r\nBENTOML_DO_NOT_TRACK=''\r\nBENTOML_CONFIG=''\r\nBENTOML_CONFIG_OPTIONS=''\r\nBENTOML_PORT=''\r\nBENTOML_HOST=''\r\nBENTOML_API_WORKERS=''\r\n```\r\n\r\n#### System information\r\n\r\n`bentoml`: 1.1.6\r\n`python`: 3.10.12\r\n`platform`: Linux-6.2.0-33-generic-x86_64-with-glibc2.35\r\n`uid_gid`: 1000:1000\r\n<details><summary><code>pip_packages</code></summary>\r\n\r\n<br>\r\n\r\n```\r\naiohttp==3.8.5\r\naiosignal==1.3.1\r\nannotated-types==0.5.0\r\nanyio==3.7.1\r\nappdirs==1.4.4\r\nasgiref==3.7.2\r\nasync-timeout==4.0.3\r\nattrs==23.1.0\r\nbentoml==1.1.6\r\nbuild==1.0.3\r\ncattrs==23.1.2\r\ncertifi==2023.7.22\r\ncharset-normalizer==3.2.0\r\ncircus==0.18.0\r\nclick==8.1.7\r\nclick-option-group==0.5.6\r\ncloudpickle==2.2.1\r\ncontextlib2==21.6.0\r\ndeepmerge==1.1.0\r\nDeprecated==1.2.14\r\nexceptiongroup==1.1.3\r\nfastapi==0.103.1\r\nfrozenlist==1.4.0\r\nfs==2.4.16\r\nh11==0.14.0\r\nidna==3.4\r\nimportlib-metadata==6.0.1\r\ninflection==0.5.1\r\nJinja2==3.1.2\r\njsonschema==4.19.1\r\njsonschema-specifications==2023.7.1\r\nmarkdown-it-py==3.0.0\r\nMarkupSafe==2.1.3\r\nmdurl==0.1.2\r\nmultidict==6.0.4\r\nnumpy==1.26.0\r\nopenapi==1.1.0\r\nopentelemetry-api==1.18.0\r\nopentelemetry-instrumentation==0.39b0\r\nopentelemetry-instrumentation-aiohttp-client==0.39b0\r\nopentelemetry-instrumentation-asgi==0.39b0\r\nopentelemetry-sdk==1.18.0\r\nopentelemetry-semantic-conventions==0.39b0\r\nopentelemetry-util-http==0.39b0\r\npackaging==23.1\r\npathspec==0.11.2\r\npip-requirements-parser==32.0.1\r\npip-tools==7.3.0\r\nprometheus-client==0.17.1\r\npsutil==5.9.5\r\npydantic==2.4.1\r\npydantic_core==2.10.1\r\nPygments==2.16.1\r\npynvml==11.5.0\r\npyparsing==3.1.1\r\npyproject_hooks==1.0.0\r\npython-dateutil==2.8.2\r\npython-json-logger==2.0.7\r\npython-multipart==0.0.6\r\nPyYAML==6.0.1\r\npyzmq==25.1.1\r\nreferencing==0.30.2\r\nrequests==2.31.0\r\nrich==13.5.3\r\nrpds-py==0.10.3\r\nschema==0.7.5\r\nsimple-di==0.1.5\r\nsix==1.16.0\r\nsniffio==1.3.0\r\nstarlette==0.27.0\r\ntomli==2.0.1\r\ntornado==6.3.3\r\ntyping_extensions==4.8.0\r\nurllib3==2.0.5\r\nuvicorn==0.23.2\r\nwatchfiles==0.20.0\r\nwrapt==1.15.0\r\nyarl==1.9.2\r\nzipp==3.17.0\r\n```\r\n\r\n</details>\r\n\r\n\n", "before_files": [{"content": "from __future__ import annotations\n\nimport typing as t\nfrom functools import lru_cache\nfrom http import HTTPStatus\nfrom typing import TYPE_CHECKING\n\nfrom deepmerge.merger import Merger\n\nfrom bentoml.exceptions import InternalServerError\nfrom bentoml.exceptions import InvalidArgument\nfrom bentoml.exceptions import NotFound\n\nfrom ...types import LazyType\nfrom ...utils import bentoml_cattr\nfrom .specification import Components\nfrom .specification import Contact\nfrom .specification import Info\nfrom .specification import MediaType\nfrom .specification import OpenAPISpecification\nfrom .specification import Operation\nfrom .specification import PathItem\nfrom .specification import Reference\nfrom .specification import Response\nfrom .specification import Tag\nfrom .utils import REF_PREFIX\nfrom .utils import exception_components_schema\nfrom .utils import exception_schema\n\nif TYPE_CHECKING:\n    from .. import Service\n    from ..inference_api import InferenceAPI\n\nSUCCESS_DESCRIPTION = \"Successful Response\"\n\nINFRA_DECRIPTION = {\n    \"/healthz\": \"Health check endpoint. Expecting an empty response with status code <code>200</code> when the service is in health state. The <code>/healthz</code> endpoint is <b>deprecated</b>. (since Kubernetes v1.16)\",\n    \"/livez\": \"Health check endpoint for Kubernetes. Healthy endpoint responses with a <code>200</code> OK status.\",\n    \"/readyz\": \"A <code>200</code> OK status from <code>/readyz</code> endpoint indicated the service is ready to accept traffic. From that point and onward, Kubernetes will use <code>/livez</code> endpoint to perform periodic health checks.\",\n    \"/metrics\": \"Prometheus metrics endpoint. The <code>/metrics</code> responses with a <code>200</code>. The output can then be used by a Prometheus sidecar to scrape the metrics of the service.\",\n}\n\n__all__ = [\"generate_spec\"]\n\nINFRA_TAG = Tag(\n    name=\"Infrastructure\",\n    description=\"Common infrastructure endpoints for observability.\",\n)\nAPP_TAG = Tag(\n    name=\"Service APIs\", description=\"BentoML Service API endpoints for inference.\"\n)\n\nmerger = Merger(\n    # merge dicts\n    [(dict, \"merge\")],\n    # override all other types\n    [\"override\"],\n    # override conflicting types\n    [\"override\"],\n)\n\n\ndef make_api_path(api: InferenceAPI[t.Any]) -> str:\n    return api.route if api.route.startswith(\"/\") else f\"/{api.route}\"\n\n\n@lru_cache(maxsize=1)\ndef make_infra_endpoints() -> dict[str, PathItem]:\n    return {\n        endpoint: PathItem(\n            get=Operation(\n                responses={\"200\": Response(description=SUCCESS_DESCRIPTION)},\n                tags=[INFRA_TAG.name],\n                description=INFRA_DECRIPTION[endpoint],\n            )\n        )\n        for endpoint in INFRA_DECRIPTION\n    }\n\n\ndef generate_service_components(svc: Service) -> Components:\n    components: dict[str, t.Any] = {}\n    for api in svc.apis.values():\n        api_components = {}\n        input_components = api.input.openapi_components()\n        if input_components:\n            merger.merge(api_components, input_components)\n        output_components = api.output.openapi_components()\n        if output_components:\n            merger.merge(api_components, output_components)\n\n        merger.merge(components, api_components)\n\n    # merge exception at last\n    merger.merge(components, {\"schemas\": exception_components_schema()})\n\n    return Components(**components)\n\n\ndef generate_spec(svc: Service, *, openapi_version: str = \"3.0.2\"):\n    \"\"\"Generate a OpenAPI specification for a service.\"\"\"\n    mounted_app_paths = {}\n\n    for app, _, _ in svc.mount_apps:\n        if LazyType[\"fastapi.FastAPI\"](\"fastapi.FastAPI\").isinstance(app):\n            from fastapi.openapi.utils import get_openapi\n\n            openapi = get_openapi(\n                title=app.title,\n                version=app.version,\n                routes=app.routes,\n            )\n\n            mounted_app_paths.update(\n                {\n                    k: bentoml_cattr.structure(v, PathItem)\n                    for k, v in openapi[\"paths\"].items()\n                }\n            )\n\n    return OpenAPISpecification(\n        openapi=openapi_version,\n        tags=[APP_TAG, INFRA_TAG],\n        components=generate_service_components(svc),\n        info=Info(\n            title=svc.name,\n            description=svc.doc,\n            version=svc.tag.version if svc.tag and svc.tag.version else \"None\",\n            contact=Contact(name=\"BentoML Team\", email=\"contact@bentoml.com\"),\n        ),\n        servers=[{\"url\": \".\"}],\n        paths={\n            # setup infra endpoints\n            **make_infra_endpoints(),\n            # setup inference endpoints\n            **{\n                make_api_path(api): PathItem(\n                    post={\n                        \"responses\": {\n                            HTTPStatus.OK.value: api.output.openapi_responses(),\n                            **{\n                                ex.error_code.value: Response(\n                                    description=filled.description,\n                                    content={\n                                        \"application/json\": MediaType(\n                                            schema=Reference(\n                                                f\"{REF_PREFIX}{filled.title}\"\n                                            )\n                                        )\n                                    },\n                                )\n                                for ex in [\n                                    InvalidArgument,\n                                    NotFound,\n                                    InternalServerError,\n                                ]\n                                for filled in exception_schema(ex)\n                            },\n                        },\n                        \"tags\": [APP_TAG.name],\n                        \"consumes\": [api.input.mime_type],\n                        \"produces\": [api.output.mime_type],\n                        \"x-bentoml-name\": api.name,\n                        \"summary\": str(api),\n                        \"description\": api.doc or \"\",\n                        \"requestBody\": api.input.openapi_request_body(),\n                        \"operationId\": f\"{svc.name}__{api.name}\",\n                    },\n                )\n                for api in svc.apis.values()\n            },\n            **mounted_app_paths,\n        },\n    )\n", "path": "src/bentoml/_internal/service/openapi/__init__.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport typing as t\nfrom functools import lru_cache\nfrom http import HTTPStatus\nfrom typing import TYPE_CHECKING\n\nfrom deepmerge.merger import Merger\n\nfrom bentoml.exceptions import InternalServerError\nfrom bentoml.exceptions import InvalidArgument\nfrom bentoml.exceptions import NotFound\n\nfrom ...types import LazyType\nfrom ...utils import bentoml_cattr\nfrom .specification import Contact\nfrom .specification import Info\nfrom .specification import MediaType\nfrom .specification import OpenAPISpecification\nfrom .specification import Operation\nfrom .specification import PathItem\nfrom .specification import Reference\nfrom .specification import Response\nfrom .specification import Tag\nfrom .utils import REF_PREFIX\nfrom .utils import exception_components_schema\nfrom .utils import exception_schema\n\nif TYPE_CHECKING:\n    from .. import Service\n    from ..inference_api import InferenceAPI\n\nSUCCESS_DESCRIPTION = \"Successful Response\"\n\nINFRA_DECRIPTION = {\n    \"/healthz\": \"Health check endpoint. Expecting an empty response with status code <code>200</code> when the service is in health state. The <code>/healthz</code> endpoint is <b>deprecated</b>. (since Kubernetes v1.16)\",\n    \"/livez\": \"Health check endpoint for Kubernetes. Healthy endpoint responses with a <code>200</code> OK status.\",\n    \"/readyz\": \"A <code>200</code> OK status from <code>/readyz</code> endpoint indicated the service is ready to accept traffic. From that point and onward, Kubernetes will use <code>/livez</code> endpoint to perform periodic health checks.\",\n    \"/metrics\": \"Prometheus metrics endpoint. The <code>/metrics</code> responses with a <code>200</code>. The output can then be used by a Prometheus sidecar to scrape the metrics of the service.\",\n}\n\n__all__ = [\"generate_spec\"]\n\nINFRA_TAG = Tag(\n    name=\"Infrastructure\",\n    description=\"Common infrastructure endpoints for observability.\",\n)\nAPP_TAG = Tag(\n    name=\"Service APIs\", description=\"BentoML Service API endpoints for inference.\"\n)\n\nmerger = Merger(\n    # merge dicts\n    [(dict, \"merge\")],\n    # override all other types\n    [\"override\"],\n    # override conflicting types\n    [\"override\"],\n)\n\n\ndef make_api_path(api: InferenceAPI[t.Any]) -> str:\n    return api.route if api.route.startswith(\"/\") else f\"/{api.route}\"\n\n\n@lru_cache(maxsize=1)\ndef make_infra_endpoints() -> dict[str, PathItem]:\n    return {\n        endpoint: PathItem(\n            get=Operation(\n                responses={\"200\": Response(description=SUCCESS_DESCRIPTION)},\n                tags=[INFRA_TAG.name],\n                description=INFRA_DECRIPTION[endpoint],\n            )\n        )\n        for endpoint in INFRA_DECRIPTION\n    }\n\n\ndef generate_service_components(svc: Service) -> dict[str, t.Any]:\n    components: dict[str, t.Any] = {}\n    for api in svc.apis.values():\n        api_components = {}\n        input_components = api.input.openapi_components()\n        if input_components:\n            merger.merge(api_components, input_components)\n        output_components = api.output.openapi_components()\n        if output_components:\n            merger.merge(api_components, output_components)\n\n        merger.merge(components, api_components)\n\n    # merge exception at last\n    return merger.merge(components, {\"schemas\": exception_components_schema()})\n\n\ndef generate_spec(svc: Service, *, openapi_version: str = \"3.0.2\"):\n    \"\"\"Generate a OpenAPI specification for a service.\"\"\"\n    mounted_app_paths = {}\n    schema_components = {}\n\n    for app, _, _ in svc.mount_apps:\n        if LazyType[\"fastapi.FastAPI\"](\"fastapi.FastAPI\").isinstance(app):\n            from fastapi.openapi.utils import get_openapi\n\n            openapi = get_openapi(\n                title=app.title,\n                version=app.version,\n                routes=app.routes,\n            )\n\n            mounted_app_paths.update(\n                {\n                    k: bentoml_cattr.structure(v, PathItem)\n                    for k, v in openapi[\"paths\"].items()\n                }\n            )\n\n            if \"components\" in openapi:\n                merger.merge(schema_components, openapi[\"components\"])\n\n    merger.merge(schema_components, generate_service_components(svc))\n\n    return OpenAPISpecification(\n        openapi=openapi_version,\n        tags=[APP_TAG, INFRA_TAG],\n        components=schema_components,\n        info=Info(\n            title=svc.name,\n            description=svc.doc,\n            version=svc.tag.version if svc.tag and svc.tag.version else \"None\",\n            contact=Contact(name=\"BentoML Team\", email=\"contact@bentoml.com\"),\n        ),\n        servers=[{\"url\": \".\"}],\n        paths={\n            # setup infra endpoints\n            **make_infra_endpoints(),\n            # setup inference endpoints\n            **{\n                make_api_path(api): PathItem(\n                    post={\n                        \"responses\": {\n                            HTTPStatus.OK.value: api.output.openapi_responses(),\n                            **{\n                                ex.error_code.value: Response(\n                                    description=filled.description,\n                                    content={\n                                        \"application/json\": MediaType(\n                                            schema=Reference(\n                                                f\"{REF_PREFIX}{filled.title}\"\n                                            )\n                                        )\n                                    },\n                                )\n                                for ex in [\n                                    InvalidArgument,\n                                    NotFound,\n                                    InternalServerError,\n                                ]\n                                for filled in exception_schema(ex)\n                            },\n                        },\n                        \"tags\": [APP_TAG.name],\n                        \"consumes\": [api.input.mime_type],\n                        \"produces\": [api.output.mime_type],\n                        \"x-bentoml-name\": api.name,\n                        \"summary\": str(api),\n                        \"description\": api.doc or \"\",\n                        \"requestBody\": api.input.openapi_request_body(),\n                        \"operationId\": f\"{svc.name}__{api.name}\",\n                    },\n                )\n                for api in svc.apis.values()\n            },\n            **mounted_app_paths,\n        },\n    )\n", "path": "src/bentoml/_internal/service/openapi/__init__.py"}]}