alessandro trinca tornidor commited on
Commit
2dbe20c
·
1 Parent(s): d77c086

feat: switch from docker sdk to gradio dsk, add [email protected]

Browse files
Files changed (5) hide show
  1. README.md +28 -116
  2. app.py +259 -0
  3. packages.txt +2 -0
  4. requirements.txt +1 -0
  5. resources +1 -0
README.md CHANGED
@@ -3,141 +3,53 @@ title: SamGIS - LISA on CUDA
3
  emoji: 🗺️
4
  colorFrom: red
5
  colorTo: blue
6
- sdk: docker
7
- pinned: false
 
 
8
  license: mit
9
  ---
10
 
11
- # [LISA](https://github.com/dvlab-research/LISA) and [SamGIS](https://github.com/trincadev/samgis-be)
12
 
13
- [LISA](https://github.com/dvlab-research/LISA) (Reasoning Segmentation via Large Language Model) applied to geospatial data thanks to [SamGIS](https://github.com/trincadev/samgis-be).
14
 
15
- ## Segment Anything models
 
16
 
17
- It's possible to prepare the model files using https://github.com/vietanhdev/samexporter/ or using the ones
18
- from https://huggingface.co/aletrn/sam-quantized (copy them within the folder `/machine_learning_models`).
19
 
20
- ## SamGIS - HuggingFace version
21
 
22
- The SamGIS HuggingSpace url is https://huggingface.co/spaces/aletrn/samgis-lisa-on-cuda.
23
- Build the docker image this way:
24
 
25
  ```bash
26
- # clean any old active containers
27
- docker stop $(docker ps -a -q); docker rm $(docker ps -a -q)
28
-
29
- # build the base docker image from the repository root folder using ARGs:
30
- # - DEPENDENCY_GROUP=fastapi used by poetry
31
- # VITE__MAP_DESCRIPTION, VITE__SAMGIS_SPACE used by 'docker build'
32
- (
33
- set -o allexport && source <(cat ./static/.env|grep VITE__) && set +o allexport;
34
- env|grep VITE__;
35
- docker build . -f dockerfiles/dockerfile-samgis-base --progress=plain \
36
- --build-arg DEPENDENCY_GROUP=fastapi \
37
- --build-arg VITE__MAP_DESCRIPTION=${VITE__MAP_DESCRIPTION} \
38
- --build-arg VITE__SAMGIS_SPACE=${VITE__SAMGIS_SPACE} \
39
- --tag registry.gitlab.com/aletrn/gis-prediction
40
- )
41
-
42
- # build the image, use the tag "samgis-huggingface"
43
- docker build . --tag example-docker-namespace/samgis-huggingface --progress=plain
44
- ```
45
-
46
- Run the container (keep it on background) and show logs
47
-
48
- ```bash
49
- docker run -d --name samgis-huggingface -p 7860:7860 example-docker-namespace/samgis-huggingface; docker logs -f samgis-huggingface
50
  ```
51
 
52
- Test it with curl using a json payload:
53
 
54
  ```bash
55
- URL=http://localhost:7860/infer_samgis
56
- curl -d@./events/payload_point_eolie.json -H 'accept: application/json' ${URL}
 
 
 
 
 
57
  ```
58
 
59
- or better visiting the swagger page on http://localhost:7860/docs
60
-
61
- ### Dependencies installation and local tests
62
- The docker build process needs only the base dependency group plus the `aws_lambda` or `fastapi` optional one.
63
- Install also the `test` and/or `docs` groups if needed.
64
-
65
- ### Tests
66
 
67
- Tests are defined in the `tests` folder in this project. Use PIP to install the test dependencies and run tests.
68
 
69
  ```bash
70
- python -m pytest --cov=samgis_lisa_on_cuda --cov-report=term-missing && coverage html
71
  ```
72
 
73
- ### How to update the static documentation with sphinx
74
-
75
- This project documentation uses sphinx-apidoc: it's a tool for automatic generation of Sphinx sources that, using the autodoc
76
- extension, document a whole package in the style of other automatic API documentation tools. See the
77
- [documentation page](https://www.sphinx-doc.org/en/master/man/sphinx-apidoc.html) for details.
78
- Run the command from the project root:
79
-
80
- ```bash
81
- # missing docs folder (run from project root) initialize this way
82
- cd docs && sphinx-quickstart -p SamGIS -r 1.0.0 -l python --master index
83
-
84
- # update docs folder (from project root)
85
- sphinx-apidoc -f -o docs samgis
86
- ```
87
-
88
- Then it's possible to generate the HTML pages
89
-
90
- ```bash
91
- cd docs && make html && ../
92
-
93
- # to clean old files
94
- cd docs && make clean html && cd ../
95
- ```
96
-
97
- The static documentation it's now ready at the path `docs/_build/html/index.html`.
98
-
99
- To create a work in progress openapi json or yaml file use
100
-
101
- - `extract-openapi-fastapi.py`
102
- - `extract-openapi-lambda.py` (useful to export the json schema request and response from lambda app api)
103
-
104
- ### Handle dynamic folder creation
105
-
106
- it's possible to dynamically create a new support folder adding it to a json string env FOLDERS_MAP, e.g.:
107
-
108
- ```json
109
- {
110
- "WORKDIR": "/var/task",
111
- "XDG_CACHE_HOME": "/data",
112
- "PROJECT_ROOT_FOLDER": "/data",
113
- "MPLCONFIGDIR": "/data/.cache/matplotlib",
114
- "TRANSFORMERS_CACHE": "/data/.cache/transformers",
115
- "PYTORCH_KERNEL_CACHE_PATH": "/data/.cache/torch/kernels",
116
- "FASTAPI_STATIC": "/var/task/static",
117
- "VIS_OUTPUT": "/data/vis_output"
118
- }
119
- ```
120
-
121
- The python script create_folders_and_variables_if_not_exists.py will read this env variable, removing any files that exists with these pathnames and assert the correct creation of all the folders. Also these folders must exist as env variables, so the script assert that an env variable exists with its path.
122
-
123
- It's possible to use the project in a bare metal installation (not within a docker container). To do this
124
-
125
- - download this project
126
- - prepare a virtualenv and install the python dependencies
127
- - install nodejs LTS
128
- - create a .env_source file (in this case `HOME=/home/jovyan`)
129
-
130
- ```bash
131
- export FOLDERS_MAP='{"WORKDIR":"/home/jovyan/workspace/samgis-lisa-on-cuda","XDG_CACHE_HOME":"/home/jovyan/.cache","PROJECT_ROOT_FOLDER":"/home/jovyan/","MPLCONFIGDIR":"/home/jovyan/.cache/matplotlib","TRANSFORMERS_CACHE":"/home/jovyan/.cache/transformers","PYTORCH_KERNEL_CACHE_PATH":"/home/jovyan/.cache/torch/kernels","FASTAPI_STATIC":"/home/jovyan/workspace/samgis-lisa-on-cuda/static","VIS_OUTPUT":"/home/jovyan/workspace/samgis-lisa-on-cuda/vis_output"}'
132
- export WORKDIR="$HOME/workspace/samgis-lisa-on-cuda"
133
- export XDG_CACHE_HOME="$HOME/.cache"
134
- export PROJECT_ROOT_FOLDER="$HOME/"
135
- export MPLCONFIGDIR="$HOME/.cache/matplotlib"
136
- export TRANSFORMERS_CACHE="$HOME/.cache/transformers"
137
- export PYTORCH_KERNEL_CACHE_PATH="$HOME/.cache/torch/kernels"
138
- export FASTAPI_STATIC="$HOME/workspace/samgis-lisa-on-cuda/static"
139
- export VIS_OUTPUT="$HOME/workspace/samgis-lisa-on-cuda/vis_output"
140
- export WRITE_TMP_ON_DISK=${VIS_OUTPUT}
141
- ```
142
 
143
- - execute the script `baremetal_entrypoint.sh` instead than `docker_entrypoint.sh`.
 
 
3
  emoji: 🗺️
4
  colorFrom: red
5
  colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.40.0
8
+ app_file: app.py
9
+ pinned: true
10
  license: mit
11
  ---
12
 
13
+ # [LISA](https://github.com/dvlab-research/LISA) + [SamGIS](https://github.com/trincadev/samgis-be) on a dedicated CUDA GPU
14
 
15
+ This project aims to permit use of [LISA](https://github.com/dvlab-research/LISA) (Reasoning Segmentation via Large Language Model) applied to geospatial data thanks to [SamGIS](https://github.com/trincadev/samgis-be). In this space I adapted LISA to HuggingFace [lisa-on-cuda](https://huggingface.co/spaces/aletrn/lisa-on-cuda) ZeroGPU space.
16
 
17
+ This [home page project](https://huggingface.co/spaces/aletrn/samgis-lisa-on-zero) is a plane Gradio interface that take a json in input to translate it to a geojson. More information about these API implementation [here](
18
+ https://aletrn-samgis-lisa-on-zero.hf.space/docs). On this [blog page](https://trinca.tornidor.com/projects/lisa-adapted-for-samgis) you can find more details, including some request and response examples with the geojson map representations.
19
 
20
+ You can also find the alternative map interface [here](https://aletrn-samgis-lisa-on-zero.hf.space/lisa/) useful to create on the fly the payload requests and to represent the geojson response.
 
21
 
22
+ ## Custom environment variables for HuggingFace CUDA Space
23
 
24
+ Fundamental environment variables you need are:
 
25
 
26
  ```bash
27
+ XDG_CACHE_HOME="/data/.cache"
28
+ PROJECT_ROOT_FOLDER="/home/user/app"
29
+ WORKDIR="/home/user/app"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  ```
31
 
32
+ Derived ones:
33
 
34
  ```bash
35
+ MPLCONFIGDIR="/data/.cache/matplotlib"
36
+ TRANSFORMERS_CACHE="/data/.cache/transformers"
37
+ PYTORCH_KERNEL_CACHE_PATH="/data/.cache/torch/kernels"
38
+ FASTAPI_STATIC="/home/user/app/static"
39
+ VIS_OUTPUT="/home/user/app/vis_output"
40
+ MODEL_FOLDER="/home/user/app/machine_learning_models"
41
+ FOLDERS_MAP='{"WORKDIR":"/home/user/app","XDG_CACHE_HOME":"/data/.cache","PROJECT_ROOT_FOLDER":"/home/user/app","MPLCONFIGDIR":"/data/.cache/matplotlib","TRANSFORMERS_CACHE":"/data/.cache/transformers","PYTORCH_KERNEL_CACHE_PATH":"/data/.cache/torch/kernels","FASTAPI_STATIC":"/home/user/app/static","VIS_OUTPUT":"/home/user/app/vis_output"}'
42
  ```
43
 
44
+ The function `build_frontend()` from lisa_on_cuda package create all the folders required for this project using the environment variable `FOLDERS_MAP`. That's useful for cache folders (XDG_CACHE_HOME, MPLCONFIGDIR, TRANSFORMERS_CACHE, PYTORCH_KERNEL_CACHE_PATH) because missing these can slow down the inference process. Also you could keep these folders in a permanent storage disk mounted on a custom path.
 
 
 
 
 
 
45
 
46
+ To change the base relative url for custom frontend add the VITE_PREFIX environment variable, e.g.:
47
 
48
  ```bash
49
+ VITE_INDEX_URL="/custom-url"
50
  ```
51
 
52
+ ## About HuggingFace space dependencies
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ For this demo simply installing `samgis-lisa` already brings all the needed dependencies.
55
+ Now `lisa.lisa_predict()` has the optional argument `inference_decorator` useful in case of use on ZeroGPU hardware or similar.
app.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Callable, NoReturn
5
+
6
+ from asgi_correlation_id import CorrelationIdMiddleware
7
+ import gradio as gr
8
+ from starlette.responses import JSONResponse
9
+ import structlog
10
+ import uvicorn
11
+ from dotenv import load_dotenv
12
+ from fastapi import FastAPI, HTTPException, Request, status
13
+ from fastapi.exceptions import RequestValidationError
14
+ from fastapi.responses import FileResponse, HTMLResponse
15
+ from fastapi.staticfiles import StaticFiles
16
+ from fastapi.templating import Jinja2Templates
17
+ from pydantic import ValidationError
18
+ from samgis_core.utilities import create_folders_if_not_exists
19
+ from samgis_core.utilities import frontend_builder
20
+ from samgis_core.utilities.session_logger import setup_logging
21
+ from samgis_web.utilities.constants import GRADIO_EXAMPLES_TEXT_LIST, GRADIO_MARKDOWN, GRADIO_EXAMPLE_BODY_STRING_PROMPT
22
+ from samgis_web.utilities.type_hints import StringPromptApiRequestBody
23
+
24
+
25
+ load_dotenv()
26
+ project_root_folder = Path(globals().get("__file__", "./_")).absolute().parent
27
+ workdir = Path(os.getenv("WORKDIR", project_root_folder))
28
+ model_folder = Path(project_root_folder / "machine_learning_models")
29
+
30
+ log_level = os.getenv("LOG_LEVEL", "INFO")
31
+ setup_logging(log_level=log_level)
32
+ app_logger = structlog.stdlib.get_logger()
33
+ app_logger.info(f"PROJECT_ROOT_FOLDER:{project_root_folder}, WORKDIR:{workdir}.")
34
+
35
+ folders_map = os.getenv("FOLDERS_MAP", "{}")
36
+ markdown_text = os.getenv("MARKDOWN_TEXT", "")
37
+ examples_text_list = os.getenv("EXAMPLES_TEXT_LIST", "").split("\n")
38
+ example_body = json.loads(os.getenv("EXAMPLE_BODY", "{}"))
39
+ mount_gradio_app = bool(os.getenv("MOUNT_GRADIO_APP", ""))
40
+
41
+ static_dist_folder = workdir / "static" / "dist"
42
+ input_css_path = os.getenv("INPUT_CSS_PATH", "src/input.css")
43
+ vite_gradio_url = os.getenv("VITE_GRADIO_URL", "/gradio")
44
+ vite_index_url = os.getenv("VITE_INDEX_URL", "/")
45
+ vite_samgis_url = os.getenv("VITE_SAMGIS_URL", "/samgis")
46
+ vite_lisa_url = os.getenv("VITE_LISA_URL", "/lisa")
47
+ fastapi_title = "samgis-lisa-on-zero2"
48
+ app = FastAPI(title=fastapi_title, version="1.0")
49
+
50
+
51
+ @app.middleware("http")
52
+ async def request_middleware(request, call_next):
53
+ from samgis_web.web.middlewares import logging_middleware
54
+
55
+ return await logging_middleware(request, call_next)
56
+
57
+
58
+ def get_example_complete(example_text):
59
+ example_dict = dict(**GRADIO_EXAMPLE_BODY_STRING_PROMPT)
60
+ example_dict["string_prompt"] = example_text
61
+ return json.dumps(example_dict)
62
+
63
+
64
+ def get_gradio_interface_geojson(fn_inference: Callable):
65
+ with gr.Blocks() as gradio_app:
66
+ gr.Markdown(GRADIO_MARKDOWN)
67
+
68
+ with gr.Row():
69
+ with gr.Column():
70
+ text_input = gr.Textbox(lines=1, placeholder=None, label="Payload input")
71
+ btn = gr.Button(value="Submit")
72
+ with gr.Column():
73
+ text_output = gr.Textbox(lines=1, placeholder=None, label="Geojson Output")
74
+
75
+ gr.Examples(
76
+ examples=[
77
+ get_example_complete(example) for example in GRADIO_EXAMPLES_TEXT_LIST
78
+ ],
79
+ inputs=[text_input],
80
+ )
81
+ btn.click(
82
+ fn_inference,
83
+ inputs=[text_input],
84
+ outputs=[text_output]
85
+ )
86
+ return gradio_app
87
+
88
+
89
+ def handle_exception_response(exception: Exception) -> NoReturn:
90
+ import subprocess
91
+ project_root_folder_content = subprocess.run(
92
+ f"ls -l {project_root_folder}/", shell=True, universal_newlines=True, stdout=subprocess.PIPE
93
+ )
94
+ app_logger.error(f"project_root folder 'ls -l' command output: {project_root_folder_content.stdout}.")
95
+ workdir_folder_content = subprocess.run(
96
+ f"ls -l {workdir}/", shell=True, universal_newlines=True, stdout=subprocess.PIPE
97
+ )
98
+ app_logger.error(f"workdir folder 'ls -l' command stdout: {workdir_folder_content.stdout}.")
99
+ app_logger.error(f"workdir folder 'ls -l' command stderr: {workdir_folder_content.stderr}.")
100
+ app_logger.error(f"inference error:{exception}.")
101
+ raise HTTPException(
102
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error on inference"
103
+ )
104
+
105
+
106
+ @app.get("/health")
107
+ async def health() -> JSONResponse:
108
+ from samgis_web.__version__ import __version__ as version_web
109
+ from samgis_core.__version__ import __version__ as version_core
110
+ from lisa_on_cuda.__version__ import __version__ as version_lisa_on_cuda
111
+ from samgis_lisa.__version__ import __version__ as version_samgis_lisa
112
+
113
+ app_logger.info(f"still alive, version_web:{version_web}, version_core:{version_core}.")
114
+ app_logger.info(f"still alive, version_lisa_on_cuda:{version_lisa_on_cuda}, version_samgis_lisa:{version_samgis_lisa}.")
115
+ return JSONResponse(status_code=200, content={"msg": "still alive..."})
116
+
117
+
118
+ def infer_lisa_gradio(request_input: StringPromptApiRequestBody) -> str:
119
+ from samgis_lisa.io_package.wrappers_helpers import get_parsed_bbox_points_with_string_prompt
120
+ from samgis_lisa.prediction_api import lisa
121
+ from samgis_lisa.utilities.constants import LISA_INFERENCE_FN
122
+
123
+ app_logger.info("starting lisa inference request...")
124
+
125
+ try:
126
+ import time
127
+
128
+ time_start_run = time.time()
129
+ body_request = get_parsed_bbox_points_with_string_prompt(request_input)
130
+ app_logger.info(f"lisa body_request:{body_request}.")
131
+ try:
132
+ source = body_request["source"]
133
+ source_name = body_request["source_name"]
134
+ app_logger.debug(f"body_request:type(source):{type(source)}, source:{source}.")
135
+ app_logger.debug(f"body_request:type(source_name):{type(source_name)}, source_name:{source_name}.")
136
+ app_logger.debug(f"lisa module:{lisa}.")
137
+ output = lisa.lisa_predict(
138
+ bbox=body_request["bbox"], prompt=body_request["prompt"], zoom=body_request["zoom"],
139
+ source=source, source_name=source_name, inference_function_name_key=LISA_INFERENCE_FN
140
+ )
141
+ duration_run = time.time() - time_start_run
142
+ app_logger.info(f"duration_run:{duration_run}.")
143
+ body = {
144
+ "duration_run": duration_run,
145
+ "output": output
146
+ }
147
+ dumped = json.dumps(body)
148
+ app_logger.info(f"json.dumps(body) type:{type(dumped)}, len:{len(dumped)}.")
149
+ app_logger.debug(f"complete json.dumps(body):{dumped}.")
150
+ return dumped
151
+ except Exception as inference_exception:
152
+ app_logger.error(f"inference_exception:{inference_exception}.")
153
+ app_logger.error(f"inference_exception, request_input:{request_input}.")
154
+ raise HTTPException(status_code=500, detail="Internal Server Error")
155
+ except ValidationError as va1:
156
+ app_logger.error(f"validation error: {str(va1)}.")
157
+ app_logger.error(f"ValidationError, request_input:{request_input}.")
158
+ raise RequestValidationError("Unprocessable Entity")
159
+
160
+
161
+ @app.post("/infer_lisa")
162
+ def infer_lisa(request_input: StringPromptApiRequestBody) -> JSONResponse:
163
+ dumped = infer_lisa_gradio(request_input=request_input)
164
+ app_logger.info(f"json.dumps(body) type:{type(dumped)}, len:{len(dumped)}.")
165
+ app_logger.debug(f"complete json.dumps(body):{dumped}.")
166
+ return JSONResponse(status_code=200, content={"body": dumped})
167
+
168
+
169
+ @app.exception_handler(RequestValidationError)
170
+ def request_validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
171
+ from samgis_web.web import exception_handlers
172
+
173
+ return exception_handlers.request_validation_exception_handler(request, exc)
174
+
175
+
176
+ @app.exception_handler(HTTPException)
177
+ def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
178
+ from samgis_web.web import exception_handlers
179
+
180
+ return exception_handlers.http_exception_handler(request, exc)
181
+
182
+
183
+ create_folders_if_not_exists.folders_creation(folders_map)
184
+ write_tmp_on_disk = os.getenv("WRITE_TMP_ON_DISK", "")
185
+ app_logger.info(f"write_tmp_on_disk:{write_tmp_on_disk}.")
186
+ if bool(write_tmp_on_disk):
187
+ try:
188
+ assert Path(write_tmp_on_disk).is_dir()
189
+ app.mount("/vis_output", StaticFiles(directory=write_tmp_on_disk), name="vis_output")
190
+ templates = Jinja2Templates(directory=str(project_root_folder / "static"))
191
+
192
+ @app.get("/vis_output", response_class=HTMLResponse)
193
+ def list_files(request: Request):
194
+
195
+ files = os.listdir(write_tmp_on_disk)
196
+ files_paths = sorted([f"{request.url._url}/{f}" for f in files])
197
+ print(files_paths)
198
+ return templates.TemplateResponse(
199
+ "list_files.html", {"request": request, "files": files_paths}
200
+ )
201
+ except (AssertionError, RuntimeError) as rerr:
202
+ app_logger.error(f"{rerr} while loading the folder write_tmp_on_disk:{write_tmp_on_disk}...")
203
+ raise rerr
204
+
205
+ frontend_builder.build_frontend(
206
+ project_root_folder=workdir,
207
+ input_css_path=input_css_path,
208
+ output_dist_folder=static_dist_folder
209
+ )
210
+ app_logger.info("build_frontend ok!")
211
+
212
+ templates = Jinja2Templates(directory="templates")
213
+
214
+ app.mount("/static", StaticFiles(directory=static_dist_folder, html=True), name="static")
215
+ # important: the index() function and the app.mount MUST be at the end
216
+ # samgis.html
217
+ app.mount(vite_samgis_url, StaticFiles(directory=static_dist_folder, html=True), name="samgis")
218
+
219
+
220
+ @app.get(vite_samgis_url)
221
+ async def samgis() -> FileResponse:
222
+ return FileResponse(path=str(static_dist_folder / "samgis.html"), media_type="text/html")
223
+
224
+
225
+ # lisa.html
226
+ app.mount(vite_lisa_url, StaticFiles(directory=static_dist_folder, html=True), name="lisa")
227
+
228
+
229
+ @app.get(vite_lisa_url)
230
+ async def lisa() -> FileResponse:
231
+ return FileResponse(path=str(static_dist_folder / "lisa.html"), media_type="text/html")
232
+
233
+
234
+ # index.html (lisa.html copy)
235
+ app.mount(vite_index_url, StaticFiles(directory=static_dist_folder, html=True), name="index")
236
+
237
+
238
+ @app.get(vite_index_url)
239
+ async def index() -> FileResponse:
240
+ return FileResponse(path=str(static_dist_folder / "index.html"), media_type="text/html")
241
+
242
+
243
+ app_logger.info(f"creating gradio interface...")
244
+ gr_interface = get_gradio_interface_geojson(infer_lisa_gradio)
245
+ app_logger.info(f"gradio interface created, mounting gradio app on url {vite_gradio_url} within FastAPI...")
246
+ app = gr.mount_gradio_app(app, gr_interface, path=vite_gradio_url)
247
+ app_logger.info("mounted gradio app within fastapi")
248
+
249
+ # add the CorrelationIdMiddleware AFTER the @app.middleware("http") decorated function to avoid missing request id
250
+ app.add_middleware(CorrelationIdMiddleware)
251
+
252
+
253
+ if __name__ == '__main__':
254
+ try:
255
+ uvicorn.run(host="0.0.0.0", port=7860, app=app)
256
+ except Exception as ex:
257
+ app_logger.error(f"fastapi/gradio application {fastapi_title}, exception:{ex}.")
258
+ print(f"fastapi/gradio application {fastapi_title}, exception:{ex}.")
259
+ raise ex
packages.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ npm
2
+ nodejs
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ samgis-lisa==1.0.1
resources ADDED
@@ -0,0 +1 @@
 
 
1
+ static/resources