The Tumor Sense
inference API.
A FastAPI server backing the workshops on this site: SVM tabular predictions, five image-classification networks, and a RAG-grounded explanation endpoint built on LangChain. Everything is JSON in, JSON out — interactive Swagger UI lives at http://localhost:8000/docs.
Three calls, end-to-end
Health check → SVM predict → RAG explanation, in one go. Substitute $API with the base URL above.
export API="http://localhost:8000"
# 1. is the server up?
curl -s $API/health | jq
# 2. predict — minimum payload is just the kernel id; other features get
# imputed with the dataset mean.
curl -s -X POST $API/api/predict \
-H "Content-Type: application/json" \
-d '{
"model": "rbf",
"features": {
"radius_mean": 17.2,
"concavity_mean": 0.18,
"concave_points_mean": 0.10
}
}' | jq
# 3. explain — feed the prediction back in.
curl -s -X POST $API/api/explain \
-H "Content-Type: application/json" \
-d '{
"model": "rbf",
"features": { "radius_mean": 17.2, "concavity_mean": 0.18 },
"prediction": {
"label": "malignant",
"decision": 1.34,
"probability_malignant": 0.91,
"confidence": 0.91
},
"top_k": 4
}' | jqMeta
Service info, schema, and the raw training metrics file.
Service health
Reports which SVM kernels, image models, and RAG components are loaded. Use this as a deploy smoke check.
{
"status": "ok",
"svm_models_loaded": ["rbf", "linear", "poly", "sigmoid"],
"image_models_available": ["baseline_cnn", "vgg16", "mobilenet_v2", "resnet18", "vit_tiny"],
"rag_ready": true
}Feature schema
The 10 WDBC 'mean' features the workshop exposes, in canonical order.
{
"feature_names": [
"radius_mean", "texture_mean", "perimeter_mean",
"area_mean", "smoothness_mean", "compactness_mean",
"concavity_mean", "concave_points_mean",
"symmetry_mean", "fractal_dimension_mean"
]
}SVM training metrics
Raw contents of backend/outputs/svm_out/metrics.json: per-kernel best_params, CV AUC, test AUC, confusion matrices and full classification reports.
{
"rbf": { "best_params": { "C": 10, "gamma": 0.01 }, "cv_auc": 0.9962, "test_auc": 0.9977, "confusion_matrix": [[41,1],[1,71]], "classification_report": { … } },
"linear": { … },
"poly": { … },
"sigmoid":{ … }
}SVM
Inference against the four scikit-learn SVMs trained on the WDBC dataset.
List SVM kernels
Returns the kernels backed by trained joblib bundles in backend/models.
{ "available_kernels": ["rbf", "linear", "poly", "sigmoid"] }SVM inference
Run a single prediction. Pass any subset of the 10 mean features — missing ones are imputed with the dataset mean. The original 30 WDBC features are reconstructed before scaling (the trained SVMs expect 30-dim input).
{
"model": "rbf",
"features": {
"radius_mean": 17.2,
"perimeter_mean": 113.0,
"area_mean": 904.0,
"concavity_mean": 0.18,
"concave_points_mean": 0.10
}
}{
"label": "malignant",
"decision": 1.34,
"probability_malignant": 0.91,
"confidence": 0.91,
"meta": {
"kernel": "rbf",
"latency_ms": 1.2,
"n_support_vectors": 78,
"model_path": "backend/models/model_rbf.joblib"
}
}Image classification
PyTorch image classification across BaselineCNN, VGG-16, MobileNet-V2, ResNet-18, and ViT-Tiny.
List image architectures
Returns the 5 image-classification models, with weights paths and whether each one has been lazily loaded into memory yet.
[
{ "id": "baseline_cnn", "name": "Baseline 3-layer CNN", "family": "cnn", "params": 297218, "weights_path": ".../BaselineCNN_weights.pth", "weights_loaded": false },
{ "id": "vgg16", "name": "VGG-16 (slim head)", "family": "vgg", … },
{ "id": "mobilenet_v2", "name": "MobileNet-V2", "family": "mobilenet", … },
{ "id": "resnet18", "name": "ResNet-18 (custom fc)","family": "resnet", … },
{ "id": "vit_tiny", "name": "ViT-Tiny (224 upsample)","family": "vit", … }
]Image inference · base64
Accepts a base64-encoded 50×50 RGB patch (or any size — it's resized internally) and returns class probabilities + latency.
{
"model": "resnet18",
"image_base64": "iVBORw0KGgoAAAANSUh…"
}{
"label": "cancer",
"predicted_class": 1,
"probability_no_cancer": 0.07,
"probability_cancer": 0.93,
"confidence": 0.93,
"meta": {
"model": "resnet18",
"latency_ms": 12.4
}
}Image inference · multipart
Same as /api/image/predict but takes a raw image upload (multipart form-data). Useful for curl-from-the-terminal demos.
curl -X POST $API/api/image/predict-file \
-F "model=resnet18" \
-F "file=@patch.png"// identical schema to /api/image/predictRAG
LangChain retrieval-augmented explanations, grounded in the corpus under backend/documents/.
RAG-grounded explanation
Builds a natural-language rationale from the prediction + the user's adjusted features, grounded in the markdown corpus under backend/documents/. Returns the explanation plus the top-K retrieved passages with their similarity scores. Uses an OpenAI chat model when OPENAI_API_KEY is set, otherwise falls back to a deterministic template generator (still grounded on retrieval).
{
"model": "rbf",
"features": {
"radius_mean": 17.2,
"concavity_mean": 0.18
},
"prediction": {
"label": "malignant",
"decision": 1.34,
"probability_malignant": 0.91,
"confidence": 0.91
},
"top_k": 4
}{
"explanation": "The model flagged this case as malignant with high confidence…",
"bullets": ["radius_mean is above the malignant centroid", "…"],
"retrieved": [
{
"id": "01_wolberg_mangasarian_1990",
"title": "Wolberg & Mangasarian (1990): Multisurface method…",
"source": "01_wolberg_mangasarian_1990.md",
"summary": "Mean radius / perimeter / concave-points dominate…",
"quote": "Across 569 fine-needle aspirates …",
"tags": ["radius_mean", "perimeter_mean", "concave_points_mean"],
"score": 0.812
}
],
"generator": "openai",
"meta": { "model": "gpt-4o-mini", "retrieved_count": 4 }
}Errors
standard FastAPI shapeUnknown model id, unknown feature name, or malformed image bytes.
Returned by /api/metrics when the metrics.json file isn't bundled.
A model weight file is missing, or the LLM/embeddings backend isn't installed.
Catch-all for unexpected inference failures. Check the server logs.
{ "detail": "human-readable message" }CORS & auth
CORS
Origin allow-list is configured via the TUMORSENSE_CORS_ORIGINS env var (comma-separated). Defaults to * for local development.
Auth
No authentication today — this server is meant for the in-site workshops and local exploration. For public deployment, gate behind a reverse proxy or sit it behind Cloudflare Access.