# MRI Inference API Base URL (local): `http://localhost:7860` Base URL (HF Space): `https://.hf.space` --- ## Endpoints ### `GET /models` Returns the list of available model checkpoint names. **Response** ```json { "models": ["small3dresnet_centiloid_v1"] } ``` --- ### `POST /inference` Runs centiloid regression on an uploaded MRI scan and stores the result. Re-submitting the same `filename` + `model_name` pair **overwrites** the existing row. **Request** — `multipart/form-data` | Field | Type | Required | Description | |---|---|---|---| | `file` | file | yes | `.nii`, `.nii.gz`, or `.tar` / `.tar.gz` containing a `.nii` | | `model_name` | string | yes | Must match a name returned by `GET /models` | | `label` | string | no | Ground-truth centiloid label (for tracking) | | `fold` | integer | no | Optional fold id, `0`-`10`, for train/val/test analysis splits | **curl** ```bash curl -X POST http://localhost:7860/inference \ -F "file=@subject_001.nii.gz" \ -F "model_name=small3dresnet_centiloid_v1" \ -F "label=38.5" ``` **Python (requests)** ```python import requests with open("subject_001.nii.gz", "rb") as f: r = requests.post( "http://localhost:7860/inference", files={"file": f}, data={"model_name": "small3dresnet_centiloid_v1", "label": "38.5"}, ) print(r.json()) ``` **Response `200`** ```json { "id": 1, "filename": "subject_001.nii.gz", "model_name": "small3dresnet_centiloid_v1", "centiloid": 42.317, "raw_output": 0.648231, "label": "38.5", "fold": null } ``` | Field | Description | |---|---| | `centiloid` | Predicted centiloid value (inverse-transformed: `sinh(raw_output) × 50`) | | `raw_output` | Raw model output in asinh-transformed space | | `label` | Ground-truth label as provided, or `null` | | `fold` | Optional fold id, or `null` | **Errors** | Status | Reason | |---|---| | `400` | Empty file | | `404` | `model_name` not found in `checkpoints/` | | `422` | Preprocessing or inference failed (bad NIfTI, no valid voxels, etc.) | --- ### `GET /results` Returns paginated past inference results, most recent first. Query params: | Param | Type | Default | Description | |---|---:|---:|---| | `limit` | integer | `250` | Page size, max `1000` | | `offset` | integer | `0` | Row offset | | `fold` | integer | none | Optional fold filter, `0`-`10` | **curl** ```bash curl "http://localhost:7860/results?limit=250&offset=0" ``` **Response `200`** ```json { "count": 2, "limit": 250, "offset": 0, "has_more": false, "results": [ { "id": 2, "filename": "subject_002.nii.gz", "model_name": "small3dresnet_centiloid_v1", "centiloid": 87.14, "raw_output": 1.053812, "label": null, "fold": null, "created_at": "2026-05-24T10:31:00.123456" }, { "id": 1, "filename": "subject_001.nii.gz", "model_name": "small3dresnet_centiloid_v1", "centiloid": 42.317, "raw_output": 0.648231, "label": "38.5", "fold": 2, "created_at": "2026-05-24T10:28:44.987654" } ] } ``` --- ### `GET /results/done` Returns only completed `(filename, model_name)` pairs for resume scripts. ```bash curl http://localhost:7860/results/done ``` --- ### `GET /results/folds` Returns the fold ids currently present in the DB. ```json { "folds": [0, 1, 2] } ``` --- ### `PATCH /results/fold` Updates fold values in bulk. Use either `id` or `filename + model_name`. Set `fold` to `null` to clear it. ```bash curl -X PATCH http://localhost:7860/results/fold \ -H "Content-Type: application/json" \ -d '{"updates":[{"filename":"subject_001.nii.gz","model_name":"small3dresnet_centiloid_v1","fold":2}]}' ``` ```json { "updated": 1, "missing": [] } ``` --- ## Adding a New Model 1. Place the `.ckpt` file in the `checkpoints/` directory. 2. The file stem becomes the `model_name` — e.g. `checkpoints/small3dresnet_v2.ckpt` → `"small3dresnet_v2"`. 3. No restart required; `GET /models` picks it up dynamically. > **Note:** Checkpoints must be PyTorch Lightning `.ckpt` files saved from `CentiloidRegressorModule`. The API extracts `hyper_parameters.model_config` and `hyper_parameters.train_config` automatically.