Spaces:
Sleeping
Sleeping
| """High-risk customer alert engine.""" | |
| import os | |
| from datetime import datetime | |
| from collections import deque | |
| import joblib | |
| import numpy as np | |
| import pandas as pd | |
| from fastapi import APIRouter, Query | |
| from src.api.schemas import HighRiskCustomer, HighRiskResponse | |
| from src.api.streaming import event_queue | |
| router = APIRouter() | |
| # Load model artifacts | |
| MODEL_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "models") | |
| _model = joblib.load(os.path.join(MODEL_DIR, "best_model.joblib")) | |
| _feature_names = joblib.load(os.path.join(MODEL_DIR, "feature_names.joblib")) | |
| _shap_explainer = joblib.load(os.path.join(MODEL_DIR, "shap_explainer.joblib")) | |
| # Alert history | |
| alert_history: deque = deque(maxlen=500) | |
| def get_high_risk_customers(threshold: float = Query(0.8, ge=0.0, le=1.0)): | |
| """Scan recent streaming events and return customers with P(churn) > threshold.""" | |
| high_risk = [] | |
| for i, event in enumerate(event_queue): | |
| customer = event["customer"] | |
| df = pd.DataFrame([customer])[_feature_names] | |
| prob = float(_model.predict_proba(df)[0, 1]) | |
| if prob > threshold: | |
| # Get top risk factors via SHAP | |
| shap_values = _shap_explainer.shap_values(df) | |
| impacts = { | |
| name: round(float(val), 4) | |
| for name, val in zip(_feature_names, shap_values[0]) | |
| } | |
| # Top 5 by absolute impact | |
| top_factors = dict( | |
| sorted(impacts.items(), key=lambda x: abs(x[1]), reverse=True)[:5] | |
| ) | |
| entry = HighRiskCustomer( | |
| customer_index=i, | |
| churn_probability=round(prob, 4), | |
| risk_level="Critical" if prob > 0.9 else "High", | |
| top_risk_factors=top_factors, | |
| ) | |
| high_risk.append(entry) | |
| # Log alert | |
| alert_history.append({ | |
| "timestamp": datetime.now().isoformat(), | |
| "customer_index": i, | |
| "churn_probability": round(prob, 4), | |
| "risk_level": entry.risk_level, | |
| }) | |
| return HighRiskResponse( | |
| threshold=threshold, | |
| count=len(high_risk), | |
| customers=high_risk, | |
| ) | |
| def get_alert_history(limit: int = Query(50, ge=1, le=500)): | |
| """Get recent alert history.""" | |
| alerts = list(alert_history)[-limit:] | |
| return {"count": len(alerts), "alerts": alerts} | |
| def clear_alert_history(): | |
| """Clear alert history.""" | |
| alert_history.clear() | |
| return {"status": "cleared"} | |