v1 API on real Ray Serve (v0.6.0) (#46) #104
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ================================= | |
| # Copyright: CEA-LIST/DIASI/SIALV | |
| # Author : pixano@cea.fr | |
| # License: CECILL-C | |
| # ================================= | |
| name: Test Backend | |
| on: | |
| push: | |
| paths: | |
| - "src/**" | |
| - "packages/**" | |
| - "tests/**" | |
| - ".github/workflows/test_back.yml" | |
| - "pyproject.toml" | |
| - ".pre-commit-config.yaml" | |
| branches: | |
| - "main" | |
| pull_request: | |
| paths: | |
| - "src/**" | |
| - "packages/**" | |
| - "tests/**" | |
| - ".github/workflows/test_back.yml" | |
| - "pyproject.toml" | |
| - ".pre-commit-config.yaml" | |
| branches: | |
| - "main" | |
| permissions: | |
| contents: read | |
| jobs: | |
| python_test: | |
| name: Backend | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13"] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| # Install the CPU torch build so the model code paths (sam2/transformers) are | |
| # actually exercised instead of silently skipped via importorskip. | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| # Install torch AND torchvision from the same CPU index so their compiled ops stay | |
| # ABI-coherent (open_clip / transformers pull torchvision; a PyPI torchvision against | |
| # a CPU-index torch breaks with "operator torchvision::nms does not exist"). | |
| python -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu | |
| # The client is a hard core dep not yet on PyPI, so install it editable BEFORE `pip install .`. | |
| python -m pip install -e packages/pixano-inference-client | |
| python -m pip install .[test,transformers] | |
| python -m pip install -e examples/numpy_detector # example entry-point plugin | |
| python -m pip install -e packages/pixano-inference-sam # SAM2 plugin (fake predictor in tests; sam-2 lib not needed) | |
| python -m pip install -e packages/pixano-inference-clip # CLIP embedding plugin (open_clip only needed for the integration test) | |
| # Fast unit tests (the Ray Serve integration suite runs in its own job below). | |
| - name: Test with pytest | |
| run: | | |
| pytest --cov=pixano_inference --cov-report=xml -m "not integration" tests/ | |
| - name: Upload coverage reports to Codecov with GitHub Action | |
| uses: codecov/codecov-action@v4.2.0 | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| integration: | |
| name: Ray Serve integration (CPU) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: "pip" | |
| # Integration tests boot a real local Ray + Serve runtime with a numpy-only stub | |
| # model (no torch, no GPU) and exercise deploy -> RUNNING -> predict -> undeploy. | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| # torch + torchvision from the same index (open_clip needs matching torchvision ops). | |
| python -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu | |
| python -m pip install -e packages/pixano-inference-client # hard core dep, not yet on PyPI | |
| python -m pip install .[test] | |
| python -m pip install -e examples/numpy_detector # example entry-point plugin | |
| python -m pip install -e packages/pixano-inference-clip # CLIP plugin: real MobileCLIP2 deploy+embed test | |
| - name: Run integration tests | |
| run: | | |
| pytest -m integration tests/integration/ | |
| docker_smoke: | |
| name: Docker build & smoke (CPU, framework-free) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| # Build a minimal, framework-free image (no torch, no SAM) with the numpy example | |
| # plugin, so the Dockerfile mechanics + entrypoint + healthcheck + Serve deploy path | |
| # are validated fast, without pulling multi-GB torch/sam-2. | |
| - name: Build minimal image | |
| run: | | |
| docker build \ | |
| --build-arg TORCH_INDEX_URL= \ | |
| --build-arg PIXANO_EXTRAS= \ | |
| --build-arg INSTALL_SAM=false \ | |
| --build-arg INSTALL_EXAMPLE=true \ | |
| -t pixano-inference:smoke . | |
| - name: Run container and smoke-test /v1/ready | |
| run: | | |
| docker run -d --name pi -p 7463:7463 --shm-size=2g \ | |
| -v "$PWD/docker/models.numpy.py:/config/models.py:ro" \ | |
| pixano-inference:smoke --host 0.0.0.0 --port 7463 --config /config/models.py | |
| for i in $(seq 1 40); do | |
| if curl -fsS http://localhost:7463/health >/dev/null 2>&1; then echo "healthy"; break; fi | |
| sleep 3 | |
| done | |
| curl -fsS http://localhost:7463/v1/ready \ | |
| | python3 -c "import sys, json; d = json.load(sys.stdin); print(d); sys.exit(0 if d['ready'] else 1)" \ | |
| || (docker logs pi; exit 1) | |
| docker rm -f pi | |
| framework_free_core: | |
| name: Framework-free core | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.13"] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| # Deliberately install NO ML framework (no torch/jax/tensorflow/mlx). The core | |
| # must import and the guardrail tests must pass with numpy only. | |
| - name: Install core only | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e packages/pixano-inference-client # hard core dep, not yet on PyPI | |
| python -m pip install .[test] | |
| - name: Assert core imports pull no ML framework | |
| run: | | |
| pytest tests/test_core_framework_free.py -v | |
| # A framework-free custom model, installed as a plugin package and discovered via its | |
| # entry point — the recommended extension path, exercised here with no ML framework. | |
| - name: Install and discover the example plugin (no ML framework) | |
| run: | | |
| python -m pip install -e examples/numpy_detector | |
| pytest tests/test_plugins.py -v | |
| openapi: | |
| name: OpenAPI schema in sync | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| # Generate the schema in the LOCKED environment (uv.lock) so the check is deterministic: | |
| # a fresh `pip install` resolves newer FastAPI/pydantic that emit a subtly different schema | |
| # than the committed docs/openapi.json (which is generated against the lockfile). | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Check the committed OpenAPI schema is in sync | |
| run: | | |
| uv sync --no-default-groups | |
| uv run python scripts/gen_openapi.py --check | |
| client_standalone: | |
| name: Standalone client (no server deps) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.13"] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| # Install ONLY the standalone client (no core, no ray/fastapi/torch). Its tests assert the | |
| # import pulls in no heavy modules — the acceptance proof that third-party callers get a | |
| # small install. Also confirm the whole server stack is genuinely absent. | |
| - name: Install the client only | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e "packages/pixano-inference-client[masks]" | |
| python -m pip install pytest | |
| - name: Assert the server stack is not installed | |
| run: | | |
| python - <<'PY' | |
| import importlib.util | |
| for mod in ["ray", "fastapi", "uvicorn", "torch", "pixano_inference"]: | |
| assert importlib.util.find_spec(mod) is None, f"{mod} should not be installed" | |
| import pixano_inference_client | |
| print("standalone client OK:", pixano_inference_client.PixanoInferenceClient) | |
| PY | |
| - name: Run the client package tests | |
| run: | | |
| pytest packages/pixano-inference-client/tests -v |