> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# ModPod setup

> Configure the ModPod service for ML model serving with LightGBM, scikit-learn, and SHAP explainability.

ModPod is a standalone Python service. It does not depend on Kafka, Keycloak, or object storage — model artifacts and the model registry live on a local persistent volume.

***

## Dependencies

ModPod is self-contained. It does not require any other FlowX infrastructure service to run.

* **Persistent volume** — required for model artifacts and the SQLite registry
* **Identity provider** — not used; authentication is a static bearer token
* **Kafka** — not used in 5.7.0
* **Object storage (S3/MinIO)** — not used; models are stored on the mounted volume

***

## Capabilities

* Upload model packages as ZIP archives with a `metadata.json` manifest
* Manage model registry (list, fetch, delete by name/version)
* Load models into memory on demand
* Run inference with SHAP-based top-factor explanations
* Support for **LightGBM** (Booster text format) and **Logistic Regression** (scikit-learn, joblib-serialized)

***

## Authentication

ModPod uses a static bearer token. Set the `AUTHORIZATION_APIKEY` environment variable and include the token on every request except the health check:

```http theme={"system"}
Authorization: Bearer <AUTHORIZATION_APIKEY>
```

<Warning>
  The API key is a static secret. Store it in a Kubernetes `Secret` (or equivalent) and never commit it to source control.
</Warning>

***

## API endpoints

All endpoints are prefixed with `/modpod`.

### Health

| Method | Path                       | Auth | Description          |
| ------ | -------------------------- | ---- | -------------------- |
| `GET`  | `/modpod/info/healthcheck` | None | Service health check |

### Model upload

| Method | Path             | Auth   | Description                                                     |
| ------ | ---------------- | ------ | --------------------------------------------------------------- |
| `POST` | `/modpod/upload` | Bearer | Upload a ZIP archive containing model files and `metadata.json` |

The ZIP must contain a `metadata.json` file with `name` and `version` fields. Files are extracted to `<MODPOD_MODELS_DIR>/<name>/<version>/`.

### Model registry

| Method   | Path                              | Auth   | Description                     |
| -------- | --------------------------------- | ------ | ------------------------------- |
| `GET`    | `/modpod/models`                  | Bearer | List all registered models      |
| `GET`    | `/modpod/models/{name}`           | Bearer | Get model details and versions  |
| `GET`    | `/modpod/models/{name}/versions`  | Bearer | List versions for a given model |
| `DELETE` | `/modpod/models/{name}/{version}` | Bearer | Delete a specific model version |
| `DELETE` | `/modpod/models`                  | Bearer | Delete all models               |

### Inference

| Method | Path                        | Auth   | Description                      |
| ------ | --------------------------- | ------ | -------------------------------- |
| `POST` | `/modpod/inference/load`    | Bearer | Load a model into memory         |
| `POST` | `/modpod/inference/predict` | Bearer | Run prediction on a loaded model |

#### Predict request

```json theme={"system"}
{
  "model_name": "call-propensity",
  "model_version": "1.0",
  "model_type": "propensity",
  "input_data": {
    "feature1": 0.5,
    "feature2": "category_a",
    "feature3": 100
  }
}
```

#### Predict response

```json theme={"system"}
{
  "output_data": {
    "model": "call-propensity:1.0",
    "input": { "...": "..." },
    "result": {
      "prediction": 0.73,
      "top_factors": "feature1=0.50 (avg=0.30); feature2=category_a"
    }
  }
}
```

***

## Supported model types

| Type                    | Format                               | File name                      |
| ----------------------- | ------------------------------------ | ------------------------------ |
| **LightGBM**            | Booster text format                  | `encoders/model-{type}.txt`    |
| **Logistic Regression** | joblib-serialized scikit-learn model | `encoders/model-{type}.joblib` |

Both types produce SHAP-based top-factor explanations alongside the prediction. LightGBM uses the built-in `pred_contrib` output; Logistic Regression uses `shap.LinearExplainer`.

***

## Model package structure

Model packages are uploaded as ZIP archives with the following layout:

```text theme={"system"}
model.zip/
├── metadata.json
└── encoders/
    ├── model-{type}.txt         # LightGBM model (or .joblib for Logistic Regression)
    ├── {type}-feature-context.csv  # Feature metadata (mean, direction, type)
    ├── standard_scaler.pkl      # StandardScaler (Logistic Regression only)
    ├── shap_background.pkl      # SHAP background data (Logistic Regression only)
    └── {feature}-classes.pkl    # LabelEncoder per categorical feature (LightGBM)
```

The `metadata.json` file must contain at least `name` and `version`:

```json theme={"system"}
{
  "name": "call-propensity",
  "version": "1.0",
  "description": "Propensity-to-call scoring model"
}
```

***

## Environment variables

### Authentication

| Environment Variable   | Description                                           | Default Value |
| ---------------------- | ----------------------------------------------------- | ------------- |
| `AUTHORIZATION_APIKEY` | Bearer token required on all authenticated endpoints. | - (required)  |

### Storage

| Environment Variable  | Description                                                                          | Default Value         |
| --------------------- | ------------------------------------------------------------------------------------ | --------------------- |
| `MODPOD_MODELS_DIR`   | Directory where uploaded model artifacts are stored. Mount a persistent volume here. | `./models`            |
| `MODPOD_DATABASE_URL` | Database URL for the model registry. SQLite is used by default.                      | `sqlite:///modpod.db` |
| `MODPOD_DB_PATH`      | SQLite file path. Used only when `MODPOD_DATABASE_URL` is not set.                   | `./modpod.db`         |

### Server

| Environment Variable | Description                          | Default Value |
| -------------------- | ------------------------------------ | ------------- |
| `GUNICORN_WORKERS`   | Number of Gunicorn worker processes. | `1`           |
| `GUNICORN_TIMEOUT`   | Request timeout in seconds.          | `600`         |

<Info>
  Recommended worker count is `1`. Inference is CPU-bound and loaded models are held in per-process memory; additional workers multiply the memory footprint without increasing throughput for a single model.
</Info>

### CORS

| Environment Variable             | Description                                                                              | Default Value                       |
| -------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------- |
| `APPLICATION_CORS_ALLOW_ORIGIN`  | Comma-separated list of allowed origins. Set to the frontend origin(s) that call ModPod. | -                                   |
| `APPLICATION_CORS_ALLOW_HEADERS` | Comma-separated list of allowed request headers.                                         | Standard FlowX headers              |
| `APPLICATION_CORS_ALLOW_METHODS` | Comma-separated list of allowed HTTP methods.                                            | `GET,PUT,POST,DELETE,PATCH,OPTIONS` |

***

## Deployment

ModPod is packaged as a Docker image based on `python:3.13.3-slim-bookworm` and runs as a non-root user.

| Setting                         | Value                      |
| ------------------------------- | -------------------------- |
| Container port                  | `8080`                     |
| Health check path               | `/modpod/info/healthcheck` |
| Models directory (in container) | `/code/models`             |
| SQLite database (in container)  | `/code/modpod.db`          |

### Resource recommendations

| Resource          | Recommendation                                                     |
| ----------------- | ------------------------------------------------------------------ |
| Memory            | 512 MB – 1 GB, depending on the number and size of loaded models   |
| CPU               | 1 vCPU (inference is CPU-bound)                                    |
| Gunicorn workers  | `1`                                                                |
| Persistent volume | Minimum `5Gi` for `/code/models` (grows with the number of models) |

<Warning>
  The SQLite registry is file-based and stored on the same volume as model artifacts. To scale ModPod horizontally, switch to an external database by setting `MODPOD_DATABASE_URL` to a shared database URL.
</Warning>

***

## Validation

<Check>
  Health check returns `200 OK`:

  ```bash theme={"system"}
  curl http://<modpod-host>/modpod/info/healthcheck
  ```
</Check>

<Check>
  Authenticated listing returns an empty list (fresh install) or your registered models:

  ```bash theme={"system"}
  curl -H "Authorization: Bearer $AUTHORIZATION_APIKEY" \
    http://<modpod-host>/modpod/models
  ```
</Check>

***

## Related resources

<CardGroup cols={2}>
  <Card title="Ingress configuration" icon="network-wired" href="./ingress-configuration">
    Routing and CORS for FlowX services
  </Card>

  <Card title="Setup guides overview" icon="list" href="./setup-guides-overview">
    Full list of FlowX service setup guides
  </Card>
</CardGroup>
