diff --git a/.github/workflows/model_servers.yaml b/.github/workflows/model_servers.yaml index 1f1639cec..6d12a3223 100644 --- a/.github/workflows/model_servers.yaml +++ b/.github/workflows/model_servers.yaml @@ -28,6 +28,8 @@ jobs: model: mistral - image_name: whispercpp model: whisper-small + - image_name: object_detection_python + model: facebook/detr-resnet-101 runs-on: ubuntu-latest permissions: contents: read diff --git a/.gitignore b/.gitignore index 9bef8cace..aa0fa0d11 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ model_servers/llamacpp_python/model.gguf !models/Containerfile !models/README.md recipes/chromedriver +recipes/chrome +convert_models/converted_models +model_servers/object_detection_python/facebook diff --git a/convert_models/download_huggingface.py b/convert_models/download_huggingface.py index 4ad55662d..0c1eb07fd 100644 --- a/convert_models/download_huggingface.py +++ b/convert_models/download_huggingface.py @@ -3,9 +3,15 @@ parser = argparse.ArgumentParser() parser.add_argument("-m", "--model") +parser.add_argument("-o", "--output") args = parser.parse_args() +if not args.output: + download_path = f"converted_models/{args.model}" +else: + download_path = f"{args.output}" + snapshot_download(repo_id=args.model, - local_dir=f"converted_models/{args.model}", + local_dir=download_path, local_dir_use_symlinks=True, cache_dir=f"converted_models/cache") \ No newline at end of file diff --git a/model_servers/object_detection_python/Makefile b/model_servers/object_detection_python/Makefile new file mode 100644 index 000000000..99351224b --- /dev/null +++ b/model_servers/object_detection_python/Makefile @@ -0,0 +1,63 @@ +PORT ?= 8000 +APP ?= object_detection_python +REGIISTRY ?= quay.io +REGISTYR_ORG ?= ai-lab +COMPONENT := model_servers + +IMAGE := $(REGIISTRY)/$(REGISTYR_ORG)/$(COMPONENT)/$(APP):latest +CUDA_IMAGE := $(REGIISTRY)/$(REGISTYR_ORG)/$(COMPONENT)/$(APP)_cuda:latest +VULKAN_IMAGE := $(REGIISTRY)/$(REGISTYR_ORG)/$(COMPONENT)/$(APP)_vulkan:latest + +MODEL_NAME ?= facebook/detr-resnet-101 +MODEL_PATH := /models/facebook/detr-resnet-101 + +BIND_MOUNT_OPTIONS := ro +OS := $(shell uname -s) +SED_BUILD_COMMAND := +ifeq ($(OS),Linux) + BIND_MOUNT_OPTIONS := ro,Z +endif + +.PHONY: install +install: + pip install -r requirements.txt + +.PHONY: download-model-python +download-model-python: + pip install -r ../../convert_models/requirements.txt + python3 ../../convert_models/download_huggingface.py -m $(MODEL_NAME) + cp -r converted_models/* ../../models + rm -rf converted_models + rm -rf ../../models/cache + +.PHONY: build +build: + sed -i '' 's/EXPOSE 8000/EXPOSE ${PORT}/g' base/Containerfile + podman build -t $(IMAGE) . -f base/Containerfile + sed -i '' 's/EXPOSE ${PORT}/EXPOSE 8000/g' base/Containerfile + +.PHONY: run +run: + cd ../../models && \ + podman run -it -d -p $(PORT):$(PORT) -v ./$(MODEL_NAME):$(MODELS_PATH):$(BIND_MOUNT_OPTIONS) \ + -e MODEL_PATH=$(MODELS_PATH) -e HOST=0.0.0.0 -e PORT=$(PORT) --net=host $(IMAGE) + +.PHONY: test +test: + $(MAKE) download-model-facebook-resnet-101-for-tests + PORT=$(PORT) MODEL_NAME=$(MODEL_NAME) MODEL_PATH=$(MODEL_PATH) PORT=$(PORT) IMAGE=$(IMAGE) PULL_ALWAYS=0 pytest -s -vvv + $(MAKE) -i clean + +.PHONY: clean +clean: + -rm -rf $(MODEL_NAME) + -rm -rf .pytest_cache + -rm -rf facebook + + +.PHONY: facebook/detr-resnet-101 +facebook/detr-resnet-101: + pip install -r ../../convert_models/requirements.txt + python3.11 ../../convert_models/download_huggingface.py -m facebook/detr-resnet-101 + cp -r converted_models/facebook ./ + rm -rf converted_models/ diff --git a/recipes/computer_vision/object_detection/model_server/Containerfile b/model_servers/object_detection_python/base/Containerfile similarity index 78% rename from recipes/computer_vision/object_detection/model_server/Containerfile rename to model_servers/object_detection_python/base/Containerfile index d7b57353b..540ed2086 100644 --- a/recipes/computer_vision/object_detection/model_server/Containerfile +++ b/model_servers/object_detection_python/base/Containerfile @@ -3,6 +3,6 @@ WORKDIR /locallm COPY requirements.txt /locallm/requirements.txt RUN pip install --upgrade pip && \ pip install --no-cache-dir --upgrade -r requirements.txt -COPY object_detection_server.py object_detection_server.py +COPY ../src/object_detection_server.py object_detection_server.py EXPOSE 8000 -ENTRYPOINT [ "uvicorn", "object_detection_server:app", "--host", "0.0.0.0" ] +ENTRYPOINT [ "uvicorn", "object_detection_server:app", "--host", "0.0.0.0" ] \ No newline at end of file diff --git a/recipes/computer_vision/object_detection/model_server/requirements.txt b/model_servers/object_detection_python/requirements.txt similarity index 93% rename from recipes/computer_vision/object_detection/model_server/requirements.txt rename to model_servers/object_detection_python/requirements.txt index ca3d9f4b0..fb39f5205 100644 --- a/recipes/computer_vision/object_detection/model_server/requirements.txt +++ b/model_servers/object_detection_python/requirements.txt @@ -5,4 +5,4 @@ requests transformers torch uvicorn - +timm \ No newline at end of file diff --git a/recipes/computer_vision/object_detection/model_server/object_detection_server.py b/model_servers/object_detection_python/src/object_detection_server.py similarity index 95% rename from recipes/computer_vision/object_detection/model_server/object_detection_server.py rename to model_servers/object_detection_python/src/object_detection_server.py index 65502dc0b..83f9b9d24 100644 --- a/recipes/computer_vision/object_detection/model_server/object_detection_server.py +++ b/model_servers/object_detection_python/src/object_detection_server.py @@ -9,7 +9,7 @@ app = FastAPI() -model = os.getenv("MODEL_PATH", default="facebook/detr-resnet-101") +model = os.getenv("MODEL_PATH", default="/models/facebook/detr-resnet-101") revision = os.getenv("MODEL_REVISION", default="no_timm") processor = AutoImageProcessor.from_pretrained(model, revision=revision) diff --git a/model_servers/object_detection_python/tests/__init__.py b/model_servers/object_detection_python/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/model_servers/object_detection_python/tests/conftest.py b/model_servers/object_detection_python/tests/conftest.py new file mode 100644 index 000000000..37fbfefd2 --- /dev/null +++ b/model_servers/object_detection_python/tests/conftest.py @@ -0,0 +1,55 @@ +import pytest_container +import pytest +import os +import logging +import platform + +if 'PORT' not in os.environ: + PORT = 8000 +else: + PORT = os.environ['PORT'] + try: + PORT = int(PORT) + except: + PORT = 8000 + +if 'IMAGE' not in os.environ: + IMAGE = 'quay.io/ai-lab/model_servers/object_detection_python:latest' +else: + IMAGE = os.environ['IMAGE'] + +MODEL_NAME=os.environ['MODEL_NAME'] +MODEL_PATH=os.environ['MODEL_PATH'] + +BIND_MOUNT_OPTIONS = 'ro' +if platform.system() == 'Linux': + BIND_MOUNT_OPTIONS = 'ro,Z' + +MS = pytest_container.Container( + url=f"containers-storage:{IMAGE}", + volume_mounts=[ + pytest_container.container.BindMount( + container_path=f"{MODEL_PATH}", + host_path=f"./{MODEL_NAME}", + flags=[BIND_MOUNT_OPTIONS] + ) + ], + extra_environment_variables={ + "MODEL_PATH": f"{MODEL_PATH}", + "HOST": "0.0.0.0", + "PORT": f"{PORT}" + }, + forwarded_ports=[ + pytest_container.PortForwarding( + container_port=PORT, + host_port=PORT + ) + ], + ) + +def pytest_addoption(parser): + pytest_container.add_logging_level_options(parser) + + +def pytest_generate_tests(metafunc): + pytest_container.auto_container_parametrize(metafunc) \ No newline at end of file diff --git a/model_servers/object_detection_python/tests/requirements.txt b/model_servers/object_detection_python/tests/requirements.txt new file mode 100644 index 000000000..22fc97f27 --- /dev/null +++ b/model_servers/object_detection_python/tests/requirements.txt @@ -0,0 +1,8 @@ +pip==24.0 +pytest-container==0.4.0 +pytest-selenium==4.1.0 +pytest-testinfra==10.1.0 +pytest==8.1.1 +requests==2.31.0 +selenium==4.19.0 +tenacity==8.2.3 diff --git a/model_servers/object_detection_python/tests/test_alive.py b/model_servers/object_detection_python/tests/test_alive.py new file mode 100644 index 000000000..fcad510a0 --- /dev/null +++ b/model_servers/object_detection_python/tests/test_alive.py @@ -0,0 +1,13 @@ +import pytest_container +from .conftest import MS +import tenacity + +CONTAINER_IMAGES = [MS] + + +def test_etc_os_release_present(auto_container: pytest_container.container.ContainerData): + assert auto_container.connection.file("/etc/os-release").exists + +@tenacity.retry(stop=tenacity.stop_after_attempt(5), wait=tenacity.wait_exponential()) +def test_alive(auto_container: pytest_container.container.ContainerData, host): + host.run_expect([0],f"curl http://localhost:{auto_container.forwarded_ports[0].host_port}",).stdout.strip() diff --git a/recipes/computer_vision/object_detection/Makefile b/recipes/computer_vision/object_detection/Makefile new file mode 100644 index 000000000..13608b6e6 --- /dev/null +++ b/recipes/computer_vision/object_detection/Makefile @@ -0,0 +1,12 @@ + +APP ?= object_detection +PORT ?= 8000 + +DETR_MODEL_NAME := facebook/detr-resnet-101 + +.PHONY: download-model-detr-resnet +download-model-detr-resnet: + python3 ../../../convert_models/download_huggingface.py -m facebook/detr-resnet-101 + + +.PHONY: build-mo diff --git a/recipes/computer_vision/object_detection/client/Containerfile b/recipes/computer_vision/object_detection/app/Containerfile similarity index 100% rename from recipes/computer_vision/object_detection/client/Containerfile rename to recipes/computer_vision/object_detection/app/Containerfile diff --git a/recipes/computer_vision/object_detection/client/object_detection_client.py b/recipes/computer_vision/object_detection/app/object_detection_client.py similarity index 100% rename from recipes/computer_vision/object_detection/client/object_detection_client.py rename to recipes/computer_vision/object_detection/app/object_detection_client.py diff --git a/recipes/computer_vision/object_detection/client/requirements.txt b/recipes/computer_vision/object_detection/app/requirements.txt similarity index 100% rename from recipes/computer_vision/object_detection/client/requirements.txt rename to recipes/computer_vision/object_detection/app/requirements.txt