From 87c3d80f34bfd08308c3a65b21a82949ee7bb5ed Mon Sep 17 00:00:00 2001 From: greg pereira Date: Wed, 10 Apr 2024 16:07:48 -0700 Subject: [PATCH] abstract object detection ms to ms dir Signed-off-by: greg pereira --- .github/workflows/model_servers.yaml | 10 ++- .github/workflows/object_detection.yaml | 87 +++++++++++++++++++ .gitignore | 1 + convert_models/download_huggingface.py | 3 +- model_servers/common/Makefile.common | 15 +++- model_servers/llamacpp_python/Makefile | 5 -- .../object_detection_python/Makefile | 23 +++++ .../base}/Containerfile | 9 +- .../src}/object_detection_server.py | 4 +- .../src/requirements.txt | 36 ++++++++ .../object_detection_python/tests/__init__.py | 0 .../object_detection_python/tests/conftest.py | 56 ++++++++++++ .../tests/requirements.txt | 8 ++ .../tests/test_alive.py | 13 +++ recipes/common/Makefile.common | 8 +- .../computer_vision/object_detection/Makefile | 33 +++++++ .../object_detection/README.md | 50 ++++++----- .../{client => app}/Containerfile | 0 .../object_detection_client.py | 0 .../object_detection/app/requirements.txt | 40 +++++++++ .../object_detection/client/requirements.txt | 3 - .../model_server/requirements.txt | 8 -- .../chatbot/Makefile | 6 ++ .../tests/functional/conftest.py | 60 +++++++++---- .../tests/integration/conftest.py | 2 +- .../tests/integration/test_app.py | 4 +- 26 files changed, 412 insertions(+), 72 deletions(-) create mode 100644 .github/workflows/object_detection.yaml create mode 100644 model_servers/object_detection_python/Makefile rename {recipes/computer_vision/object_detection/model_server => model_servers/object_detection_python/base}/Containerfile (62%) rename {recipes/computer_vision/object_detection/model_server => model_servers/object_detection_python/src}/object_detection_server.py (90%) create mode 100644 model_servers/object_detection_python/src/requirements.txt create mode 100644 model_servers/object_detection_python/tests/__init__.py create mode 100644 model_servers/object_detection_python/tests/conftest.py create mode 100644 model_servers/object_detection_python/tests/requirements.txt create mode 100644 model_servers/object_detection_python/tests/test_alive.py create mode 100644 recipes/computer_vision/object_detection/Makefile rename recipes/computer_vision/object_detection/{client => app}/Containerfile (100%) rename recipes/computer_vision/object_detection/{client => app}/object_detection_client.py (100%) create mode 100644 recipes/computer_vision/object_detection/app/requirements.txt delete mode 100644 recipes/computer_vision/object_detection/client/requirements.txt delete mode 100644 recipes/computer_vision/object_detection/model_server/requirements.txt diff --git a/.github/workflows/model_servers.yaml b/.github/workflows/model_servers.yaml index 94ce2f2bd..767f3c6f4 100644 --- a/.github/workflows/model_servers.yaml +++ b/.github/workflows/model_servers.yaml @@ -50,6 +50,12 @@ jobs: directory: whispercpp platforms: linux/amd64,linux/arm64 no_gpu: 1 + - image_name: object_detection_python + model: facebook-detr-resnet-101 + flavor: base + directory: object_detection_python + platforms: linux/amd64 + no_gpu: 1 runs-on: ubuntu-latest permissions: contents: read @@ -98,7 +104,9 @@ jobs: - name: Run non-gpu tests working-directory: ./model_servers/${{ matrix.directory }}/ if: ${{ matrix.no_gpu }} - run: make test REGISTRY=${{ env.REGISTRY }} IMAGE_NAME=${{ env.REGISTRY_ORG }}/${{ matrix.image_name}}:latest + run: make test + env: + IMAGE_NAME=${{ env.REGISTRY_ORG }}/${{ matrix.image_name}}:${{ steps.build_image.outputs.tags }} # - name: Run cuda test # we dont have cuda tests # working-directory: ./model_servers/${{ matrix.directory }}/ diff --git a/.github/workflows/object_detection.yaml b/.github/workflows/object_detection.yaml new file mode 100644 index 000000000..821250110 --- /dev/null +++ b/.github/workflows/object_detection.yaml @@ -0,0 +1,87 @@ +name: object_detection + +on: + pull_request: + branches: + - main + paths: + - ./recipes/computer_vision/object_detection/** + - .github/workflows/object_detection.yaml + push: + branches: + - main + paths: + - ./recipes/computer_vision/object_detection/** + - .github/workflows/object_detection.yaml + + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: object_detection + COMPONENT: app + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + services: + registry: + image: registry:2.8.3 + ports: + - 5000:5000 + steps: + - uses: actions/checkout@v4.1.1 + + - name: Install qemu dependency + run: | + sudo apt-get update + sudo apt-get install -y qemu-user-static + + - name: Build Image + id: build_image + uses: redhat-actions/buildah-build@v2.13 + with: + image: ${{ env.REGISTRY }}/${{ github.event.repository.name }}/${{ env.COMPONENT }}/${{ env.IMAGE_NAME }} + tags: latest + platforms: linux/amd64, linux/arm64 + containerfiles: ./recipes/computer_vision/${{ env.IMAGE_NAME }}/app/Containerfile + context: recipes/computer_vision/${{ env.IMAGE_NAME }}/app + + - name: Set up Python + uses: actions/setup-python@v5.0.0 + with: + python-version: '3.11' + + - name: Install Dependencies + working-directory: ./recipes/computer_vision/${{ env.IMAGE_NAME }} + run: make install + + - name: Download model + working-directory: ./model_servers/object_detection_python + run: make download-model-facebook-detr-resnet-101 + + # COMING SOON + # - name: Run Functional Tests + # shell: bash + # run: make functional-tests + # working-directory: ./recipes/computer_vision/${{ env.IMAGE_NAME }} + + - name: Login to Registry + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: redhat-actions/podman-login@v1.7 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push Image + id: push_image + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: redhat-actions/push-to-registry@v2.8 + with: + image: ${{ steps.build_image.outputs.image }} + tags: ${{ steps.build_image.outputs.tags }} + registry: ${{ env.REGISTRY }} diff --git a/.gitignore b/.gitignore index 5e97f2843..e8d0155d3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ port_check.lock models/* !models/Makefile !models/README.md +model_servers/object_detection_python/detr-resnet-101 convert_models/converted_models recipes/common/bin/* */.venv/ diff --git a/convert_models/download_huggingface.py b/convert_models/download_huggingface.py index 4ad55662d..a0c9a37b0 100644 --- a/convert_models/download_huggingface.py +++ b/convert_models/download_huggingface.py @@ -7,5 +7,4 @@ snapshot_download(repo_id=args.model, local_dir=f"converted_models/{args.model}", - local_dir_use_symlinks=True, - cache_dir=f"converted_models/cache") \ No newline at end of file + local_dir_use_symlinks=True) \ No newline at end of file diff --git a/model_servers/common/Makefile.common b/model_servers/common/Makefile.common index ee249451c..a5f6a9f92 100644 --- a/model_servers/common/Makefile.common +++ b/model_servers/common/Makefile.common @@ -2,6 +2,12 @@ REGISTRY ?= quay.io REGISTRY_ORG ?= ai-lab COMPONENT ?= model_servers +IMAGE_NAME ?= $(REGISTRY_ORG)/$(COMPONENT)/$(APP):latest +IMAGE := $(REGISTRY)/$(IMAGE_NAME) +CUDA_IMAGE := $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP)_cuda:latest +VULKAN_IMAGE := $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP)_vulkan:latest + + BIND_MOUNT_OPTIONS := ro OS := $(shell uname -s) ifeq ($(OS),Linux) @@ -10,7 +16,8 @@ endif .PHONY: build build: - podman build --squash-all --build-arg $(PORT) -t $(IMAGE) . -f base/Containerfile + -$(MAKE) clean -i + podman build --squash-all --build-arg PORT=$(PORT) -t $(IMAGE) . -f base/Containerfile .PHONY: install install: @@ -35,13 +42,13 @@ clean: .PHONY: run run: cd ../../models && \ - podman run -it -d -p $(PORT):$(PORT) -v ./$(MODEL_NAME):$(MODELS_PATH)/$(MODEL_NAME):$(BIND_MOUNT_OPTIONS) -e MODEL_PATH=$(MODELS_PATH)/$(MODEL_NAME) -e HOST=0.0.0.0 -e PORT=$(PORT) $(IMAGE) + podman run -it -d -p $(PORT):$(PORT) -v ./$(MODEL_NAME):$(MODEL_PATH)/$(MODEL_NAME):$(BIND_MOUNT_OPTIONS) -e MODEL_PATH=$(MODEL_PATH)/$(MODEL_NAME) -e HOST=0.0.0.0 -e PORT=$(PORT) $(IMAGE) .PHONY: podman-clean podman-clean: - @container_ids=$$(podman ps --format "{{.ID}} {{.Image}}" | awk '$$2 == "$(IMAGE)" {print $$1}'); \ + @container_ids=$$(podman ps -a --format "{{.ID}} {{.Image}}" | awk '$$2 == "$(IMAGE)" {print $$1}'); \ echo "removing all containers with IMAGE=$(IMAGE)"; \ for id in $$container_ids; do \ - echo "Removing container: $$id,"; \ + echo "Removing container: $$id"; \ podman rm -f $$id; \ done diff --git a/model_servers/llamacpp_python/Makefile b/model_servers/llamacpp_python/Makefile index 883d9f150..67c88f94f 100644 --- a/model_servers/llamacpp_python/Makefile +++ b/model_servers/llamacpp_python/Makefile @@ -3,11 +3,6 @@ PORT ?= 8001 include ../common/Makefile.common -IMAGE_NAME ?= $(REGISTRY_ORG)/$(COMPONENT)/$(APP):latest -IMAGE := $(REGISTRY)/$(IMAGE_NAME) -CUDA_IMAGE := $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP)_cuda:latest -VULKAN_IMAGE := $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP)_vulkan:latest - MODELS_PATH := /locallm/models MODEL_NAME ?= mistral-7b-instruct-v0.1.Q4_K_M.gguf diff --git a/model_servers/object_detection_python/Makefile b/model_servers/object_detection_python/Makefile new file mode 100644 index 000000000..9e6bcdc4a --- /dev/null +++ b/model_servers/object_detection_python/Makefile @@ -0,0 +1,23 @@ +APP := object_detection_python +PORT ?= 8000 + +include ../../model_servers/common/Makefile.common + +MODEL_NAME ?= detr-resnet-101 +MODEL_PATH := /app/models + +.PHONY: all +all: build download-model-facebook-detr-resnet-101 run + +.PHONY: download-model-facebook-detr-resnet-101 +download-model-facebook-detr-resnet-101: + pip install -r ../../convert_models/requirements.txt + cd ../../convert_models/ && \ + python3 download_huggingface.py -m facebook/detr-resnet-101 + cp -r ../../convert_models/converted_models/facebook/detr-resnet-101 ../../models/detr-resnet-101 + +.PHONY: test +test: + $(MAKE) download-model-facebook-detr-resnet-101 + ln -s ../../models/detr-resnet-101 ./ + PORT=$(PORT) MODEL_NAME=$(MODEL_NAME) MODEL_PATH=$(MODEL_PATH) IMAGE=$(IMAGE) PULL_ALWAYS=0 pytest -s -vvv diff --git a/recipes/computer_vision/object_detection/model_server/Containerfile b/model_servers/object_detection_python/base/Containerfile similarity index 62% rename from recipes/computer_vision/object_detection/model_server/Containerfile rename to model_servers/object_detection_python/base/Containerfile index d7b57353b..ebeec3926 100644 --- a/recipes/computer_vision/object_detection/model_server/Containerfile +++ b/model_servers/object_detection_python/base/Containerfile @@ -1,8 +1,9 @@ FROM registry.access.redhat.com/ubi9/python-311:1-52 -WORKDIR /locallm -COPY requirements.txt /locallm/requirements.txt +ARG PORT=8000 +WORKDIR /app +COPY src/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 -EXPOSE 8000 +COPY src/object_detection_server.py . +EXPOSE $PORT ENTRYPOINT [ "uvicorn", "object_detection_server:app", "--host", "0.0.0.0" ] 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 90% 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..25bf462cf 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,9 @@ app = FastAPI() -model = os.getenv("MODEL_PATH", default="facebook/detr-resnet-101") +model_path = os.getenv("MODEL_PATH", default="/app/models") +model_name = os.getenv("MODEL_NAME", default="detr-resnet-101") +model = os.path.abspath(f"{model_path}/{model_name}") revision = os.getenv("MODEL_REVISION", default="no_timm") processor = AutoImageProcessor.from_pretrained(model, revision=revision) diff --git a/model_servers/object_detection_python/src/requirements.txt b/model_servers/object_detection_python/src/requirements.txt new file mode 100644 index 000000000..89e263e08 --- /dev/null +++ b/model_servers/object_detection_python/src/requirements.txt @@ -0,0 +1,36 @@ +annotated-types==0.6.0 +anyio==4.3.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +click==8.1.7 +fastapi==0.110.1 +filelock==3.13.4 +fsspec==2024.3.1 +h11==0.14.0 +huggingface-hub==0.22.2 +idna==3.7 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +mpmath==1.3.0 +networkx==3.3 +numpy==1.26.4 +packaging==24.0 +pillow==10.3.0 +pydantic==2.6.4 +pydantic_core==2.16.3 +PyYAML==6.0.1 +regex==2023.12.25 +requests==2.31.0 +safetensors==0.4.2 +sniffio==1.3.1 +starlette==0.37.2 +sympy==1.12 +timm==0.9.16 +tokenizers==0.15.2 +torch==2.2.2 +torchvision==0.17.2 +tqdm==4.66.2 +transformers==4.39.3 +typing_extensions==4.11.0 +urllib3==2.2.1 +uvicorn==0.29.0 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..f2647d617 --- /dev/null +++ b/model_servers/object_detection_python/tests/conftest.py @@ -0,0 +1,56 @@ +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 = 'ghcr.io/containers/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}/{MODEL_NAME}", + host_path=f"./{MODEL_NAME}", + flags=[BIND_MOUNT_OPTIONS] + ) + ], + extra_environment_variables={ + "MODEL_NAME": f"{MODEL_NAME}", + "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) 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/common/Makefile.common b/recipes/common/Makefile.common index a400498a0..969f630ac 100644 --- a/recipes/common/Makefile.common +++ b/recipes/common/Makefile.common @@ -5,6 +5,8 @@ IMAGE_NAME ?= $(REGISTRY_ORG)/${APP}:latest APP_IMAGE ?= $(REGISTRY)/$(IMAGE_NAME) CHROMADB_IMAGE ?= $(REGISTRY)/$(REGISTRY_ORG)/chromadb:latest MODEL_IMAGE ?= $(REGISTRY)/$(REGISTRY_ORG)/mistral-7b-instruct:latest +MODEL_SERVER_PORT ?= 8001 +MODEL_ENDPOINT ?= http://10.88.0.1:$(MODEL_SERVER_PORT) SERVER_IMAGE ?= $(REGISTRY)/$(REGISTRY_ORG)/llamacpp-python:latest SSH_PUBKEY ?= $(shell cat ${HOME}/.ssh/id_rsa.pub;) BOOTC_IMAGE ?= quay.io/$(REGISTRY_ORG)/${APP}-bootc:latest @@ -54,8 +56,8 @@ CHROME_MIRROR := https://www.slimjet.com/chrome/files/$(CHROMEDRIVER_VERSION)/$( LOCAL_CHROMEDRIVER_EXISTS ?= $(shell command -v $(RECIPE_BINARIES_PATH)/chromedriver) UNZIP_EXISTS ?= $(shell command -v unzip) -RELATIVE_MODELS_PATH := ?= -RELATIVE_TESTS_PATH := ?= +RELATIVE_MODELS_PATH ?= +RELATIVE_TESTS_PATH ?= MISTRAL_MODEL_NAME := mistral-7b-instruct-v0.1.Q4_K_M.gguf MISTRAL_MODEL_URL := https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.1-GGUF/resolve/main/mistral-7b-instruct-v0.1.Q4_K_M.gguf @@ -171,7 +173,7 @@ quadlet: .PHONY: run run: - podman run -it -p $(PORT):$(PORT) -e MODEL_ENDPOINT=http://10.88.0.1:8001 ${APP_IMAGE} + podman run -it -p $(PORT):$(PORT) -e MODEL_ENDPOINT=$(MODEL_ENDPOINT) ${APP_IMAGE} .PHONY: clean clean: diff --git a/recipes/computer_vision/object_detection/Makefile b/recipes/computer_vision/object_detection/Makefile new file mode 100644 index 000000000..5aeca0810 --- /dev/null +++ b/recipes/computer_vision/object_detection/Makefile @@ -0,0 +1,33 @@ + +APP := object_detection_client +COMPONENT := app +PORT ?= 8501 +MODEL_SERVER_PORT ?= 8000 +MODEL_ENDPOINT ?= http://10.88.0.1:$(MODEL_SERVER_PORT)/detection + +# because tests dont exist yet using app requirements +RELATIVE_TESTS_PATH := app + +include ../../common/Makefile.common + + +APP_IMAGE ?= $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP):latest + +MODEL_NAME ?= detr-resnet-101 +MODEL_PATH := /models/detr-resnet-101 + +.PHONY: all +all: build-and-run-server build run + +# .PHONY: facebook/detr-resnet-101 # THIS WILL BE USED FOR FUNCTIONAL TESTS +# 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/ + +.PHONY: build-and-run-server +build-and-run-server: + cd ../../../model_servers/object_detection_python/ && \ + $(MAKE) all + diff --git a/recipes/computer_vision/object_detection/README.md b/recipes/computer_vision/object_detection/README.md index 49cdef175..925218852 100644 --- a/recipes/computer_vision/object_detection/README.md +++ b/recipes/computer_vision/object_detection/README.md @@ -2,57 +2,61 @@ This recipe provides an example for running an object detection model service and its associated client locally. -## Build and run the model service +## Quickstart -```bash -cd object_detection/model_server -podman build -t object_detection_service . -``` +If you cant wait to get into the explanation of how everything works and just want to run the services do: -```bash -podman run -it --rm -p 8000:8000 object_detection_service -``` +`make all` + +and then navigate to `http://localhost:8501`. Happy tinkering. + +## Build and run the model service + +`make build-and-run-server` By default the model service will use [`facebook/detr-resnet-101`](https://huggingface.co/facebook/detr-resnet-101), which has an apache-2.0 license. The model is relatively small, but it will be downloaded fresh each time the model server is started unless a local model is provided (see additional instructions below). -## Use a different or local model +### Use a different or local model If you'd like to use a different model hosted on huggingface, simply use the environment variable `MODEL_PATH` and set it to the correct `org/model` path on [huggingface.co](https://huggingface.co/) when starting your container. If you'd like to download models locally so that they are not pulled each time the container restarts, you can use the following python snippet to a model to your `models/` directory. -```python -from huggingface_hub import snapshot_download - -snapshot_download(repo_id="facebook/detr-resnet-101", - revision="no_timm", - local_dir="/locallm/models/vision/object_detection/facebook/detr-resnet-101", - local_dir_use_symlinks=False) +There is a make target in the [`/model_servers/](../../../model_servers/object_detection_python/) directory that would let you download any model from huggingface by the modelname: +```bash +cd ../../../model_servers/object_detection_python/ +make download-model-python -m +# NOTE: if no model is specified it will default to `facebook/detr-resnet-101` which is the default model used throughout this recipe ``` -When using a model other than the default, you will need to set the `MODEL_PATH` environment variable. Here is an example of running the model service with a local model: +If you want to then use this custom image, you will have to call the make target again, passing the `MODEL_NAME` and `MODEL_PATH`. The `MODEL_NAME` would be whatever you downloaded it from huggingface as, and the `MODEL_PATH` is where you want to mount the model. I will pass the default models and model path as if I were using a custom model, so you can see how it works: ```bash - podman run -it --rm -p 8000:8000 -v /locallm/models/vision/:/locallm/models -e MODEL_PATH=models/object_detection/facebook/detr-resnet-50/ object_detection_service +make MODEL_NAME=facebook/detr-resnet-101 MODEL_PATH=/models/facebook/detr-resnet-101 build-and-run-server ``` +In this example the `facebook/detr-resnet-101` directories would need to be inside of the `/models` directory at the root of the repo. + ## Build and run the client application -```bash -cd object_detection/client -podman build -t object_detection_client . -``` +Building the application is simple using make: `make build` or you could build it with the Containerfile: ```bash -podman run -p 8501:8501 -e MODEL_ENDPOINT=http://10.88.0.1:8000/detection object_detection_client +podman build -t $(IMAGE) app/ ``` +If you want to provide the `PORT` variable you have to use the Makefile or be sure to edit the Containerfile first, otherwise you will run on the standard 8501 Port. + +Then we can run the app `make run`. + Once the client is up a running, you should be able to access it at `http://localhost:8501`. From here you can upload images from your local machine and detect objects in the image as shown below.

+## tests +Coming soon ... 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/app/requirements.txt b/recipes/computer_vision/object_detection/app/requirements.txt new file mode 100644 index 000000000..f49b86687 --- /dev/null +++ b/recipes/computer_vision/object_detection/app/requirements.txt @@ -0,0 +1,40 @@ +altair==5.3.0 +attrs==23.2.0 +blinker==1.7.0 +cachetools==5.3.3 +certifi==2024.2.2 +charset-normalizer==3.3.2 +click==8.1.7 +gitdb==4.0.11 +GitPython==3.1.43 +idna==3.7 +Jinja2==3.1.3 +jsonschema==4.21.1 +jsonschema-specifications==2023.12.1 +markdown-it-py==3.0.0 +MarkupSafe==2.1.5 +mdurl==0.1.2 +numpy==1.26.4 +packaging==24.0 +pandas==2.2.2 +pillow==10.3.0 +protobuf==4.25.3 +pyarrow==15.0.2 +pydeck==0.8.1b0 +Pygments==2.17.2 +python-dateutil==2.9.0.post0 +pytz==2024.1 +referencing==0.34.0 +requests==2.31.0 +rich==13.7.1 +rpds-py==0.18.0 +six==1.16.0 +smmap==5.0.1 +streamlit==1.33.0 +tenacity==8.2.3 +toml==0.10.2 +toolz==0.12.1 +tornado==6.4 +typing_extensions==4.11.0 +tzdata==2024.1 +urllib3==2.2.1 diff --git a/recipes/computer_vision/object_detection/client/requirements.txt b/recipes/computer_vision/object_detection/client/requirements.txt deleted file mode 100644 index 7b2195a0d..000000000 --- a/recipes/computer_vision/object_detection/client/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -streamlit -requests -pillow \ No newline at end of file diff --git a/recipes/computer_vision/object_detection/model_server/requirements.txt b/recipes/computer_vision/object_detection/model_server/requirements.txt deleted file mode 100644 index ca3d9f4b0..000000000 --- a/recipes/computer_vision/object_detection/model_server/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -fastapi -pillow -pydantic -requests -transformers -torch -uvicorn - diff --git a/recipes/natural_language_processing/chatbot/Makefile b/recipes/natural_language_processing/chatbot/Makefile index 94a51513c..1b2a04b33 100644 --- a/recipes/natural_language_processing/chatbot/Makefile +++ b/recipes/natural_language_processing/chatbot/Makefile @@ -7,3 +7,9 @@ include ../../common/Makefile.common RECIPE_BINARIES_PATH := $(shell realpath ../../common/bin) RELATIVE_MODELS_PATH := ../../../models RELATIVE_TESTS_PATH := ../tests + +.PHONY: install +install:: + $(MAKE) install-chromedriver CHROMEDRIVER_INSTALLATION_PATH=${CHROMEDRIVER_INSTALLATION_PATH} + $(MAKE) install-chrome CHROMEDRIVER_INSTALLATION_PATH=${CHROMEDRIVER_INSTALLATION_PATH} + pip install -q -r ${RELATIVE_TESTS_PATH}/requirements.txt \ No newline at end of file diff --git a/recipes/natural_language_processing/tests/functional/conftest.py b/recipes/natural_language_processing/tests/functional/conftest.py index 35f5137b3..32cd3fcb6 100644 --- a/recipes/natural_language_processing/tests/functional/conftest.py +++ b/recipes/natural_language_processing/tests/functional/conftest.py @@ -2,38 +2,68 @@ import os import logging -REGISTRY=os.environ['REGISTRY'] -IMAGE_NAME=os.environ['IMAGE_NAME'] -MODEL_NAME=os.environ['MODEL_NAME'] +IMAGE_NAME=os.environ['IMAGE_NAME'] # REQUIRED +MODEL_NAME=os.environ['MODEL_NAME'] # REQUIRED + +if not 'REGISTRY' in os.environ: + REGISTRY = 'quay.io' +else: + REGISTRY=os.environ['REGISTRY'] + +if not 'MODEL_SERVER_PORT' in os.environ: + MODEL_SERVER_PORT=8001 +else + MODEL_SERVER_PORT=os.environ['MODEL_SERVER_PORT'] + try: + MODEL_SERVER_PORT = int(MODEL_SERVER_PORT) + except: + MODEL_SERVER_PORT = 8001 + +if not 'MODEL_PATH' in os.environ: + MODEL_PATH='/app/models' +else + MODEL_PATH=os.environ['MODEL_PATH'] + +if not 'MODEL_ENDPOINT' in os.environ: + MODEL_ENDPOINT='10.88.0.1' +else + MODEL_ENDPOINT=os.environ['MODEL_ENDPOINT'] + +if not 'MODEL_SERVER_PORT' in os.environ: + MODEL_SERVER_PORT = 8001 +else + MODEL_SERVER_PORT = os.environ['MODEL_SERVER_PORT'] logging.info(""" Starting pytest with the following ENV vars: - REGISTRY: {REGISTRY} - IMAGE_NAME: {IMAGE_NAME} - MODEL_NAME: {MODEL_NAME} + REGISTRY: {REGISTRY} + IMAGE_NAME: {IMAGE_NAME} + MODEL_PATH: {MODEL_PATH} + MODEL_NAME: {MODEL_NAME} + MODEL_SERVER_PORT: {MODEL_SERVER_PORT} + MODEL_ENDPOINT: {MODEL_ENDPOINT} For: model_server: whispercpp -""".format(REGISTRY=REGISTRY, IMAGE_NAME=IMAGE_NAME, MODEL_NAME=MODEL_NAME)) - +""".format(REGISTRY=REGISTRY, IMAGE_NAME=IMAGE_NAME, MODEL_NAME=MODEL_NAME, MODEL_PATH=MODEL_PATH, MODEL_SERVER_PORT=MODEL_SERVER_PORT, MODEL_ENDPOINT=MODEL_ENDPOINT)) MS = pytest_container.Container( url=f"containers-storage:{REGISTRY}/{IMAGE_NAME}", volume_mounts=[ pytest_container.container.BindMount( - container_path=f"/locallm/models/${MODEL_NAME}", + container_path=f"{MODEL_PATH}/${MODEL_NAME}", host_path=f"./{MODEL_NAME}", flags=["ro"] ) ], extra_environment_variables={ - "MODEL_PATH": f"/locall/models/{MODEL_NAME}", - "HOST": "0.0.0.0", - "PORT": "8001" + "MODEL_PATH": f"{MODEL_PATH}/{MODEL_NAME}", + "HOST": "{MODEL_ENDPOINT}", + "MODEL_SERVER_PORT": "{MODEL_SERVER_PORT}" }, forwarded_ports=[ pytest_container.PortForwarding( - container_port=8001, - host_port=8001 + container_port={MODEL_SERVER_PORT}, + host_port={MODEL_SERVER_PORT} ) ], ) @@ -41,7 +71,7 @@ CB = pytest_container.Container( url=f"containers-storage:{os.environ['REGISTRY']}/containers/{os.environ['IMAGE_NAME']}", extra_environment_variables={ - "MODEL_ENDPOINT": "http://10.88.0.1:8001" + "MODEL_ENDPOINT": MODEL_ENDPOINT }, forwarded_ports=[ pytest_container.PortForwarding( diff --git a/recipes/natural_language_processing/tests/integration/conftest.py b/recipes/natural_language_processing/tests/integration/conftest.py index 3a67a71cb..da5ad9ef0 100644 --- a/recipes/natural_language_processing/tests/integration/conftest.py +++ b/recipes/natural_language_processing/tests/integration/conftest.py @@ -4,4 +4,4 @@ @pytest.fixture() def url(): - return os.environ["URL"] + return os.environ["MODEL_ENDPOINT"] diff --git a/recipes/natural_language_processing/tests/integration/test_app.py b/recipes/natural_language_processing/tests/integration/test_app.py index e2825e60c..b52e9f513 100644 --- a/recipes/natural_language_processing/tests/integration/test_app.py +++ b/recipes/natural_language_processing/tests/integration/test_app.py @@ -1,3 +1,3 @@ -def test_title(url,selenium): - selenium.get(f"http://{url}:8501") +def test_title(model_endpoint,selenium): + selenium.get(f"http://{model_endpoint}:8501") assert selenium.title == "Streamlit"