Spaces:
Sleeping
Sleeping
File size: 4,601 Bytes
742cd9a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | """
Prediction endpoints.
Thin router layer β HTTP handling only.
All business logic lives in predictor.py.
Endpoints:
POST /predict/features β predict from engineered features
GET /predict/example β return example high-risk patient
"""
import logging
from fastapi import APIRouter, HTTPException, status
from src.api.models import PatientFeatures, PredictionResponse
from src.api.predictor import predictor
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="/predict",
tags=["Prediction"],
)
@router.post(
"/features",
response_model=PredictionResponse,
summary="Predict 30-day readmission risk",
description="""
Accepts 31 engineered clinical features and returns:
- Readmission probability (0.0β1.0)
- Risk category (LOW / MEDIUM / HIGH)
- Top 5 SHAP-based risk factors with clinical descriptions
- Clinical action recommendation
All features must be computed at the point of patient discharge.
Features requiring post-discharge knowledge constitute target leakage
and must not be included.
""",
status_code=status.HTTP_200_OK,
)
async def predict_from_features(
patient: PatientFeatures,
) -> PredictionResponse:
"""
Run readmission risk prediction for a single patient.
The request body should contain all 31 engineered features.
See the /predict/example endpoint for a sample high-risk patient.
"""
if not predictor.is_loaded:
logger.error("Prediction requested but model not loaded")
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail={
"error": "Model not available",
"message": "The prediction model is not loaded. "
"Please try again in a few seconds.",
}
)
try:
logger.info(
f"Prediction request received β "
f"age={patient.age_at_admission}, "
f"los={patient.los_days}, "
f"charlson={patient.charlson_score}"
)
result = predictor.predict(patient)
logger.info(
f"Prediction complete β "
f"risk={result.risk_percent}%, "
f"category={result.risk_category}"
)
return result
except Exception as e:
logger.error(f"Prediction failed: {e}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={
"error": "Prediction failed",
"message": "An error occurred during prediction. "
"Please check your input and try again.",
}
)
@router.get(
"/example",
response_model=dict,
summary="Get example patient input",
description="Returns the highest-risk patient from model evaluation "
"as an example API input. Useful for testing.",
)
async def get_example_patient() -> dict:
"""
Return example high-risk patient features.
This is the patient from the SHAP waterfall plot β
predicted 99.6% readmission risk, actually readmitted.
"""
return {
"description": "Highest-risk patient from model evaluation",
"predicted_risk": "99.6%",
"actual_outcome": "Readmitted",
"clinical_profile": (
"Male, age 66.6, 3 prior admissions in 6 months, "
"47 medications, Charlson score 2, 29 active conditions"
),
"features": {
"los_days": 6.0,
"los_days_log": 1.946,
"admission_month": 2,
"admission_dow": 1,
"is_emergency": 0,
"n_admissions_prior_6m": 3,
"n_admissions_prior_12m": 3,
"n_ed_visits_prior_6m": 1,
"days_since_last_admission": 28.0,
"has_prior_admission": 1,
"charlson_score": 2,
"n_active_conditions": 29,
"has_heart_failure": 0,
"has_diabetes": 0,
"has_diabetes_complex": 0,
"has_copd": 0,
"has_ckd": 1,
"has_mi": 0,
"has_cancer": 1,
"has_dementia": 0,
"has_cerebrovascular": 0,
"has_pvd": 0,
"n_medications_capped": 47.0,
"is_high_polypharmacy": 1,
"has_insulin": 0,
"has_anticoagulant": 1,
"has_diuretic": 1,
"has_ace_inhibitor": 0,
"age_at_admission": 66.6,
"gender_male": 1,
"income": 125393.0
}
} |