From e0bfc3f1a8ecd0e3e72220528ccc8fe33a7688ce Mon Sep 17 00:00:00 2001 From: hhsecond Date: Mon, 12 Dec 2022 18:06:22 +0000 Subject: [PATCH 01/10] adding datatypes that can show better code --- .../components/serve/python_server.py | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index 40b7e83a3bdca..0b5c4b10d0511 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -5,6 +5,7 @@ from pathlib import Path from typing import Any, Dict, Optional +import requests import uvicorn from fastapi import FastAPI from lightning_utilities.core.imports import compare_version, module_available @@ -48,14 +49,67 @@ class Image(BaseModel): image: Optional[str] @staticmethod - def _get_sample_data() -> Dict[Any, Any]: - imagepath = Path(__file__).parent / "catimage.png" - with open(imagepath, "rb") as image_file: - encoded_string = base64.b64encode(image_file.read()) - return {"image": encoded_string.decode("UTF-8")} + def get_sample_data() -> Dict[Any, Any]: + url = "https://raw.githubusercontent.com/Lightning-AI/LAI-Triton-Server-Component/main/catimage.png" + img = requests.get(url).content + img = base64.b64encode(img).decode("UTF-8") + return {"image": img} + + @staticmethod + def request_code_sample(url: str) -> str: + return """import base64 +from pathlib import Path +import requests + +img = requests.get("https://raw.githubusercontent.com/Lightning-AI/LAI-Triton-Server-Component/main/catimage.png").content +img = base64.b64encode(img).decode("UTF-8") +response = requests.post('""" + url + """', json={ + "image": img +})""" + + @staticmethod + def response_code_sample() -> str: + return """img = response.json()["image"] +img = base64.b64decode(img.encode("utf-8")) +Path("response.png").write_bytes(img) +""" + + +class Category(BaseModel): + category: Optional[int] + + @staticmethod + def get_sample_data() -> Dict[Any, Any]: + return {"prediction": 463} + + @staticmethod + def response_code_sample() -> str: + return """print("Predicted category is: ", response.json()["category"]) +""" + + +class Text(BaseModel): + text: Optional[str] + + @staticmethod + def get_sample_data() -> Dict[Any, Any]: + return {"text": "A portrait of a person looking away from the camera"} + + @staticmethod + def request_code_sample(url: str) -> str: + return """import base64 +from pathlib import Path +import requests + +response = requests.post('""" + url + """', json={ + "text": "A portrait of a person looking away from the camera" +}) +""" class Number(BaseModel): + # deprecated + # TODO remove this in favour of Category prediction: Optional[int] @staticmethod From 8eb30764c81dc96e95d4bca24037b1ae3bc6c597 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:08:30 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../components/serve/python_server.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index 0b5c4b10d0511..d9f42a682e906 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -57,15 +57,19 @@ def get_sample_data() -> Dict[Any, Any]: @staticmethod def request_code_sample(url: str) -> str: - return """import base64 + return ( + """import base64 from pathlib import Path import requests img = requests.get("https://raw.githubusercontent.com/Lightning-AI/LAI-Triton-Server-Component/main/catimage.png").content img = base64.b64encode(img).decode("UTF-8") -response = requests.post('""" + url + """', json={ +response = requests.post('""" + + url + + """', json={ "image": img })""" + ) @staticmethod def response_code_sample() -> str: @@ -84,7 +88,7 @@ def get_sample_data() -> Dict[Any, Any]: @staticmethod def response_code_sample() -> str: - return """print("Predicted category is: ", response.json()["category"]) + return """print("Predicted category is: ", response.json()["category"]) """ @@ -97,14 +101,18 @@ def get_sample_data() -> Dict[Any, Any]: @staticmethod def request_code_sample(url: str) -> str: - return """import base64 + return ( + """import base64 from pathlib import Path import requests -response = requests.post('""" + url + """', json={ +response = requests.post('""" + + url + + """', json={ "text": "A portrait of a person looking away from the camera" }) """ + ) class Number(BaseModel): From 2e91e734c5ab40b8575ff1a6c360cd3bb02bb15c Mon Sep 17 00:00:00 2001 From: hhsecond Date: Mon, 12 Dec 2022 18:11:25 +0000 Subject: [PATCH 03/10] import fix --- src/lightning_app/components/serve/python_server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index d9f42a682e906..39ca7d5ce2ac1 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -2,7 +2,6 @@ import base64 import os import platform -from pathlib import Path from typing import Any, Dict, Optional import requests From 6380b203c048e243c4001c4f1a1f0c35b35ff016 Mon Sep 17 00:00:00 2001 From: hhsecond Date: Mon, 12 Dec 2022 19:00:15 +0000 Subject: [PATCH 04/10] imports --- src/lightning_app/components/__init__.py | 4 +++- src/lightning_app/components/serve/__init__.py | 4 ++-- src/lightning_app/components/serve/python_server.py | 6 +++--- tests/tests_app/components/serve/test_python_server.py | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/lightning_app/components/__init__.py b/src/lightning_app/components/__init__.py index ca47c36071dae..6230ad319bc1d 100644 --- a/src/lightning_app/components/__init__.py +++ b/src/lightning_app/components/__init__.py @@ -10,7 +10,7 @@ from lightning_app.components.python.popen import PopenPythonScript from lightning_app.components.python.tracer import Code, TracerPythonScript from lightning_app.components.serve.gradio import ServeGradio -from lightning_app.components.serve.python_server import Image, Number, PythonServer +from lightning_app.components.serve.python_server import Image, Number, Category, Text, PythonServer from lightning_app.components.serve.serve import ModelInferenceAPI from lightning_app.components.serve.streamlit import ServeStreamlit from lightning_app.components.training import LightningTrainerScript, PyTorchLightningScriptRunner @@ -28,6 +28,8 @@ "PythonServer", "Image", "Number", + "Category", + "Text", "MultiNode", "LiteMultiNode", "LightningTrainerScript", diff --git a/src/lightning_app/components/serve/__init__.py b/src/lightning_app/components/serve/__init__.py index cb46a71bf9ea5..95525f23a003d 100644 --- a/src/lightning_app/components/serve/__init__.py +++ b/src/lightning_app/components/serve/__init__.py @@ -1,5 +1,5 @@ from lightning_app.components.serve.gradio import ServeGradio -from lightning_app.components.serve.python_server import Image, Number, PythonServer +from lightning_app.components.serve.python_server import Image, Number, Category, Text, PythonServer from lightning_app.components.serve.streamlit import ServeStreamlit -__all__ = ["ServeGradio", "ServeStreamlit", "PythonServer", "Image", "Number"] +__all__ = ["ServeGradio", "ServeStreamlit", "PythonServer", "Image", "Number", "Category", "Text"] diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index 39ca7d5ce2ac1..da9c3f818a44e 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -120,7 +120,7 @@ class Number(BaseModel): prediction: Optional[int] @staticmethod - def _get_sample_data() -> Dict[Any, Any]: + def get_sample_data() -> Dict[Any, Any]: return {"prediction": 463} @@ -215,8 +215,8 @@ def predict(self, request: Any) -> Any: @staticmethod def _get_sample_dict_from_datatype(datatype: Any) -> dict: - if hasattr(datatype, "_get_sample_data"): - return datatype._get_sample_data() + if hasattr(datatype, "get_sample_data"): + return datatype.get_sample_data() datatype_props = datatype.schema()["properties"] out: Dict[str, Any] = {} diff --git a/tests/tests_app/components/serve/test_python_server.py b/tests/tests_app/components/serve/test_python_server.py index 313638e9ec42a..45275af9f87b7 100644 --- a/tests/tests_app/components/serve/test_python_server.py +++ b/tests/tests_app/components/serve/test_python_server.py @@ -32,14 +32,14 @@ def test_python_server_component(): def test_image_sample_data(): - data = Image()._get_sample_data() + data = Image().get_sample_data() assert isinstance(data, dict) assert "image" in data assert len(data["image"]) > 100 def test_number_sample_data(): - data = Number()._get_sample_data() + data = Number().get_sample_data() assert isinstance(data, dict) assert "prediction" in data assert data["prediction"] == 463 From e65e562c3425fd710557b6503b243cac0182a7eb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 19:01:47 +0000 Subject: [PATCH 05/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/lightning_app/components/__init__.py | 2 +- src/lightning_app/components/serve/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lightning_app/components/__init__.py b/src/lightning_app/components/__init__.py index 6230ad319bc1d..18208aa316f2d 100644 --- a/src/lightning_app/components/__init__.py +++ b/src/lightning_app/components/__init__.py @@ -10,7 +10,7 @@ from lightning_app.components.python.popen import PopenPythonScript from lightning_app.components.python.tracer import Code, TracerPythonScript from lightning_app.components.serve.gradio import ServeGradio -from lightning_app.components.serve.python_server import Image, Number, Category, Text, PythonServer +from lightning_app.components.serve.python_server import Category, Image, Number, PythonServer, Text from lightning_app.components.serve.serve import ModelInferenceAPI from lightning_app.components.serve.streamlit import ServeStreamlit from lightning_app.components.training import LightningTrainerScript, PyTorchLightningScriptRunner diff --git a/src/lightning_app/components/serve/__init__.py b/src/lightning_app/components/serve/__init__.py index 95525f23a003d..a12cb1b45ee71 100644 --- a/src/lightning_app/components/serve/__init__.py +++ b/src/lightning_app/components/serve/__init__.py @@ -1,5 +1,5 @@ from lightning_app.components.serve.gradio import ServeGradio -from lightning_app.components.serve.python_server import Image, Number, Category, Text, PythonServer +from lightning_app.components.serve.python_server import Category, Image, Number, PythonServer, Text from lightning_app.components.serve.streamlit import ServeStreamlit __all__ = ["ServeGradio", "ServeStreamlit", "PythonServer", "Image", "Number", "Category", "Text"] From a7224e7945230f9d5e5f0496fa22c89b035ab7d5 Mon Sep 17 00:00:00 2001 From: Sherin Thomas Date: Tue, 13 Dec 2022 23:38:07 +0000 Subject: [PATCH 06/10] precommit fix --- src/lightning_app/components/serve/python_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index da9c3f818a44e..d928b48ffab25 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -61,7 +61,8 @@ def request_code_sample(url: str) -> str: from pathlib import Path import requests -img = requests.get("https://raw.githubusercontent.com/Lightning-AI/LAI-Triton-Server-Component/main/catimage.png").content +imgurl = "https://raw.githubusercontent.com/Lightning-AI/LAI-Triton-Server-Component/main/catimage.png" +img = requests.get(imgurl).content img = base64.b64encode(img).decode("UTF-8") response = requests.post('""" + url From 502e9404bee9fdf06fa3fb4232ca29b43d47d26f Mon Sep 17 00:00:00 2001 From: Sherin Thomas Date: Wed, 14 Dec 2022 18:45:38 +0000 Subject: [PATCH 07/10] code samples --- .../components/serve/python_server.py | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index d928b48ffab25..1c07b0580fa36 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -249,7 +249,18 @@ def predict_fn(request: input_type): # type: ignore fastapi_app.post("/predict", response_model=output_type)(predict_fn) - def configure_layout(self) -> None: + def get_code_sample(self, url: str) -> Optional[str]: + input_type: Any = self.configure_input_type() + output_type: Any = self.configure_output_type() + + if not ( + hasattr(input_type, "request_code_sample") and + hasattr(output_type, "response_code_sample") + ): + return None + return f"{input_type.request_code_sample(url)}\n{output_type.response_code_sample()}" + + def configure_layout(self) -> Optional['APIAccessFrontend']: try: from lightning_api_access import APIAccessFrontend except ModuleNotFoundError: @@ -265,17 +276,19 @@ def configure_layout(self) -> None: except TypeError: return None - return APIAccessFrontend( - apis=[ - { - "name": class_name, - "url": url, - "method": "POST", - "request": request, - "response": response, - } - ] - ) + frontend_payload = { + "name": class_name, + "url": url, + "method": "POST", + "request": request, + "response": response, + } + + code_sample = self.get_code_sample(url) + if code_sample: + frontend_payload["code_sample"] = code_sample + + return APIAccessFrontend(apis=[frontend_payload]) def run(self, *args: Any, **kwargs: Any) -> Any: """Run method takes care of configuring and setting up a FastAPI server behind the scenes. From 2c2e8a69507baf3cbd2140777615d242a24fdf4d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 18:48:47 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/lightning_app/components/serve/python_server.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index 1c07b0580fa36..66c83be07d2a0 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -253,14 +253,11 @@ def get_code_sample(self, url: str) -> Optional[str]: input_type: Any = self.configure_input_type() output_type: Any = self.configure_output_type() - if not ( - hasattr(input_type, "request_code_sample") and - hasattr(output_type, "response_code_sample") - ): + if not (hasattr(input_type, "request_code_sample") and hasattr(output_type, "response_code_sample")): return None return f"{input_type.request_code_sample(url)}\n{output_type.response_code_sample()}" - def configure_layout(self) -> Optional['APIAccessFrontend']: + def configure_layout(self) -> Optional["APIAccessFrontend"]: try: from lightning_api_access import APIAccessFrontend except ModuleNotFoundError: From 5746f7bf0982144bde057478b3a323fae604515a Mon Sep 17 00:00:00 2001 From: Sherin Thomas Date: Fri, 16 Dec 2022 15:28:46 +0000 Subject: [PATCH 09/10] typecheck --- src/lightning_app/components/serve/python_server.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lightning_app/components/serve/python_server.py b/src/lightning_app/components/serve/python_server.py index 66c83be07d2a0..ee958b30625fd 100644 --- a/src/lightning_app/components/serve/python_server.py +++ b/src/lightning_app/components/serve/python_server.py @@ -2,7 +2,7 @@ import base64 import os import platform -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, TYPE_CHECKING import requests import uvicorn @@ -14,6 +14,9 @@ from lightning_app.utilities.app_helpers import Logger from lightning_app.utilities.imports import _is_torch_available, requires +if TYPE_CHECKING: + from lightning_app.frontend.frontend import Frontend + logger = Logger(__name__) # Skip doctests if requirements aren't available @@ -257,7 +260,7 @@ def get_code_sample(self, url: str) -> Optional[str]: return None return f"{input_type.request_code_sample(url)}\n{output_type.response_code_sample()}" - def configure_layout(self) -> Optional["APIAccessFrontend"]: + def configure_layout(self) -> Optional["Frontend"]: try: from lightning_api_access import APIAccessFrontend except ModuleNotFoundError: From 7e0f8595cb146b8261abb0aa9f0b855c9fd41cfe Mon Sep 17 00:00:00 2001 From: Sherin Thomas Date: Fri, 16 Dec 2022 15:32:01 +0000 Subject: [PATCH 10/10] change log --- src/lightning_app/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lightning_app/CHANGELOG.md b/src/lightning_app/CHANGELOG.md index 132706e539837..b427988b92400 100644 --- a/src/lightning_app/CHANGELOG.md +++ b/src/lightning_app/CHANGELOG.md @@ -12,9 +12,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added a progres bar while connecting to an app through the CLI ([#16035](https://github.com/Lightning-AI/lightning/pull/16035)) - - Added partial support for fastapi `Request` annotation in `configure_api` handlers ([#16047](https://github.com/Lightning-AI/lightning/pull/16047)) +- Added more datatypes to serving component ([#16018](https://github.com/Lightning-AI/lightning/pull/16018)) ### Changed