# APEX backend container (Phase 5 task 5.2 + wave-48 deploy fixes) # # Build: # docker build -t apex-backend:0.2.0 app/backend # # Run (local): # docker run --rm -p 8000:8000 \ # -v ${PWD}/fixtures:/srv/fixtures:ro \ # -e APEX_AUDIT_LOG_PATH=/srv/audit/audit-log.jsonl \ # -e OPENROUTER_API_KEY=$OPENROUTER_API_KEY \ # apex-backend:0.2.0 # # Smoke (after `docker run`): # curl http://localhost:8000/healthz # curl http://localhost:8000/api/session-context # curl http://localhost:8000/api/orchestration # # Production deploy targets (per Phase 5 task 5.1): # - HuggingFace Spaces (Docker SDK, free CPU tier) # 1. Create Space at huggingface.co/new-space (Docker SDK, CPU basic) # 2. Copy app/backend/* + fixtures/ into the Space repo # 3. Add README.md HF Spaces frontmatter (see app/backend/README.md) # 4. git push to huggingface.co; Spaces builds + serves automatically # - Fly.io: `fly launch --no-deploy --image apex-backend:0.2.0` # - Modal: `modal deploy` from a wrapped `modal.Image.from_dockerfile()` # # wave-48 fixes vs the v0.1.0 Dockerfile: # 1. `pip install -r requirements.txt` for real (was pinning only 4 # packages by name; cvxpylayers + transformers were silently # missing). Skipped torch+cuda; install CPU torch separately for # size. # 2. `COPY fixtures` so `/api/analyze` + `/api/orchestration` can # resolve `fixtures/personas/sarah-reynolds-*` on a fresh # container (was 503-ing because fixtures were dev-only). # 3. HEALTHCHECK extended to 60s start period to accommodate TTM lazy # load when `APEX_ENABLE_TTM=1`. FROM python:3.11-slim AS base # --- system deps ------------------------------------------------------- RUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ curl \ build-essential \ && rm -rf /var/lib/apt/lists/* WORKDIR /srv/app # --- python deps (light layer; CPU torch only, no cuda) --------------- # Install CPU-only torch first (much smaller than CUDA wheel; CPU is # what HF Spaces free tier provides). cvxpy + cvxpylayers + scs + # clarabel + transformers + granite-tsfm + httpx all install via the # main requirements.txt pin. COPY requirements.txt requirements.txt RUN pip install --no-cache-dir --upgrade pip wheel setuptools RUN pip install --no-cache-dir torch==2.5.1 --index-url https://download.pytorch.org/whl/cpu # requirements.txt was authored against Vinh's Windows+CUDA env; filter # out the torch line + install the rest. Anything we don't need at # runtime (llama_cpp_python is heavy + only used by the offline G1b # bench script, never by the FastAPI server) is dropped via the grep. # OpenTelemetry packages excluded from the lean install because # opentelemetry-instrumentation 0.60b0 pins wrapt<2.0.0 which conflicts # with Vinh's wrapt==2.2.1 pin. observability.py lazy-imports OTel + # logs a no-op warning when the packages are absent, so the server # still boots cleanly without them. Reinstate when an OTel release # accepts wrapt 2.x. RUN grep -vE "^(torch==|llama_cpp_python==|fastf1==|opentelemetry-)" requirements.txt > requirements.lean.txt \ && pip install --no-cache-dir -r requirements.lean.txt \ && pip install --no-cache-dir \ fastapi \ "uvicorn[standard]" \ python-multipart \ structlog # Wave-51d cascade-#60: install OTel packages needed for Honeycomb wire-up. # Skipping opentelemetry-instrumentation-fastapi due to wrapt<2.0 peer dep # vs Vinh's wrapt==2.2.1 pin (see comment above). observability.py wave-51d # falls back to manual request-tracing middleware when FastAPIInstrumentor # missing; still ships per-request spans with HTTP method + path + status. RUN pip install --no-cache-dir \ opentelemetry-api==1.39.0 \ opentelemetry-sdk==1.39.0 \ opentelemetry-exporter-otlp-proto-http==1.39.0 # --- app code + fixtures ---------------------------------------------- # COPY assumes the build context contains: apex/ + fixtures/ + (optional) # docs/. For HF Spaces deploy, the deploy script symlinks/copies # fixtures from `../../fixtures` into the build context first; for # repo-root `docker build -f app/backend/Dockerfile .` the COPY paths # below still resolve. Local-only `docker build app/backend` requires # `cp -r ../../fixtures app/backend/fixtures` BEFORE building. COPY apex apex COPY fixtures /srv/app/fixtures # --- runtime ---------------------------------------------------------- ENV PYTHONPATH=/srv/app \ APEX_AUDIT_LOG_PATH=/srv/audit/audit-log.jsonl \ APEX_FIXTURES_DIR=/srv/app/fixtures \ APEX_COMMIT_SHA=container \ APEX_ENABLE_TTM=0 \ PYTHONUNBUFFERED=1 RUN mkdir -p /srv/audit && chmod 0777 /srv/audit EXPOSE 7860 8000 # HF Spaces routes traffic to port 7860 by default; uvicorn binds both # to keep local docker-run + Fly.io + HF Spaces all happy. HEALTHCHECK --interval=30s --timeout=5s --start-period=60s \ CMD curl -fsS http://localhost:${PORT:-7860}/healthz || curl -fsS http://localhost:8000/healthz || exit 1 CMD ["sh", "-c", "uvicorn apex.server:app --host 0.0.0.0 --port ${PORT:-7860}"]