# Docker Deployment Instructions

Docker deployment runs the solution in an isolated container.
Best for reproducibility, isolation, and HTTP-based APIs.

## NAMES AND PORT (assigned by Kapso — use them exactly)

- image: `kapso-{deployment_name}`
- container: `kapso-{deployment_name}`
- host port: `{port}` (the container listens on 8000)

Two solutions deployed on the same machine must not share names or ports;
these are derived from this solution's path, so keep them as given.

## DEPLOY COMMAND

```bash
docker rm -f kapso-{deployment_name} 2>/dev/null; docker build -t kapso-{deployment_name} . && docker run -d --name kapso-{deployment_name} -p {port}:8000 --restart unless-stopped {env_file_flag} kapso-{deployment_name}
```

Run this command to build and start the Docker container (`rm -f` first, so a
re-deploy replaces the previous container of this solution). If it fails,
debug and fix the error.

## RUN INTERFACE
- type: http
- endpoint: http://localhost:{port}
- predict_path: /predict
- container_name: kapso-{deployment_name}
- image_name: kapso-{deployment_name}
- port: {port}

After successful deployment, output this JSON (update only what differs):
```
<run_interface>{"type": "http", "endpoint": "http://localhost:{port}", "predict_path": "/predict", "container_name": "kapso-{deployment_name}", "image_name": "kapso-{deployment_name}", "port": {port}}</run_interface>
```

## CRITICAL: YOU MUST BUILD AND TEST THE CONTAINER

**Do NOT just create files. You MUST build and verify the Docker image works.**

After creating the Dockerfile:
1. Run the DEPLOY COMMAND above
2. Verify the build succeeds without errors
3. Test with: `curl http://localhost:{port}/health`
4. Test a prediction: `curl -X POST http://localhost:{port}/predict -H "Content-Type: application/json" -d '{"test": true}'`
5. If there are errors, fix them and rebuild

**If deployment fails, debug the error and fix it. Do not give up.**

## Environment variables

Variables provided by the caller: {env_var_names}. When any are listed, a
`.env` file with them is at the workspace root; the DEPLOY COMMAND passes it
with `--env-file`, and Kapso passes the same variables when it recreates the
container. Keep `.env` out of the image (`.dockerignore` below) — never
`COPY` it or bake values into `ENV` lines.

## Required Structure

```
solution/
├── main.py           # Entry point with predict() function
├── app.py            # FastAPI application (for HTTP interface)
├── requirements.txt  # Dependencies
├── Dockerfile        # Container definition
└── .dockerignore     # Files to exclude from image
```

## Dockerfile

Create a `Dockerfile` with this content:

```dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install dependencies first (better caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Run as an unprivileged user
RUN useradd --create-home --uid 1000 app && chown -R app:app /app
USER app

# Expose port for HTTP interface
EXPOSE 8000

# Health check (python:3.11-slim ships no curl)
HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/health', timeout=2).status == 200 else 1)"

# Run the application
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
```

## FastAPI Application (app.py)

If using HTTP interface, create `app.py`:

```python
"""
FastAPI application for Docker deployment.
"""

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Any, Dict

app = FastAPI(title="Solution API", version="1.0.0")


class PredictResponse(BaseModel):
    """Output schema for predictions."""
    status: str
    output: Any = None
    error: str = None


@app.get("/health")
def health():
    """Health check endpoint."""
    return {"status": "healthy"}


@app.post("/predict", response_model=PredictResponse)
def predict(request: Dict[str, Any]):
    """
    Main prediction endpoint.
    
    IMPORTANT: Accepts raw JSON input directly (not wrapped in "data").
    Example: {"text": "hello"} NOT {"data": {"text": "hello"}}
    """
    try:
        from main import predict as _predict
        result = _predict(request)
        return PredictResponse(status="success", output=result)
    except Exception as e:
        return PredictResponse(status="error", error=str(e))
```

## .dockerignore

Create `.dockerignore` to exclude unnecessary files:

```
__pycache__
*.pyc
*.pyo
.git
.gitignore
.env
*.md
tests/
.pytest_cache/
.mypy_cache/
```

## Requirements

Add FastAPI and uvicorn to `requirements.txt`:

```
fastapi>=0.100.0
uvicorn>=0.23.0
# ... your other dependencies
```

## Testing Docker Locally

```bash
# Health endpoint
curl http://localhost:{port}/health

# Prediction endpoint (send input directly, not wrapped in "data")
curl -X POST http://localhost:{port}/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "hello"}'

# Container logs
docker logs kapso-{deployment_name}
```

## Notes

- Use multi-stage builds for smaller images if needed
- Pin base image versions for reproducibility
- Don't include secrets in the image - use env vars at runtime

