| export const API_BASE = |
| process.env.NEXT_PUBLIC_API_BASE ?? |
| (typeof window !== "undefined" && window.location.hostname !== "localhost" |
| ? "/api" |
| : "http://localhost:8000"); |
|
|
| export type PredictRequest = { |
| patient_id?: string | null; |
| age: number; |
| sex: "M" | "F"; |
| chest_pain_type: "ASY" | "ATA" | "NAP" | "TA"; |
| max_hr: number; |
| fasting_bs: 0 | 1; |
| exercise_angina: "Y" | "N"; |
| st_slope: "Up" | "Flat" | "Down"; |
| resting_bp?: number | null; |
| cholesterol?: number | null; |
| resting_ecg?: "Normal" | "ST" | "LVH" | null; |
| oldpeak?: number | null; |
| }; |
|
|
| export type PredictResponse = { |
| patient_id: string; |
| timestamp: string; |
| probability: number; |
| classification: "HIGH" | "LOW"; |
| threshold: number; |
| probabilities: [number, number]; |
| }; |
|
|
| export type HistoryEntry = { |
| date: string; |
| time: string; |
| patient_id: string; |
| age: number; |
| sex: string; |
| max_hr: number; |
| fasting_bs: string; |
| exercise_angina: string; |
| chest_pain_type: string; |
| st_slope: string; |
| probability: number; |
| classification: string; |
| risk: string; |
| }; |
|
|
| export type Stats = { |
| total: number; |
| high_risk: number; |
| low_risk: number; |
| high_risk_pct: number; |
| today: number; |
| }; |
|
|
| export type ModelInfo = { |
| name: string; |
| threshold: number; |
| metrics: { |
| accuracy: number; |
| recall: number; |
| roc_auc: number; |
| cv_mean: number; |
| cv_std: number; |
| }; |
| alternatives: { name: string; accuracy: number; recall: number; roc_auc: number }[]; |
| features: string[]; |
| }; |
|
|
| async function request<T>(path: string, init?: RequestInit): Promise<T> { |
| const res = await fetch(`${API_BASE}${path}`, { |
| ...init, |
| headers: { "Content-Type": "application/json", ...(init?.headers ?? {}) }, |
| }); |
| if (!res.ok) { |
| throw new Error(`API ${res.status}: ${await res.text()}`); |
| } |
| return res.json(); |
| } |
|
|
| export type EdaData = { |
| age_dist: { bin: string; count: number; risk_rate: number }[]; |
| sex_dist: { sex: string; count: number; risk_rate: number }[]; |
| chest_pain_dist: { type: string; count: number; risk_rate: number }[]; |
| scatter: { Age: number; MaxHR: number; HeartDisease: number }[]; |
| total: number; |
| }; |
|
|
| export const api = { |
| predict: (body: PredictRequest) => |
| request<PredictResponse>("/predict", { |
| method: "POST", |
| body: JSON.stringify(body), |
| }), |
| history: () => request<HistoryEntry[]>("/history"), |
| stats: () => request<Stats>("/stats"), |
| modelInfo: () => request<ModelInfo>("/model-info"), |
| eda: () => request<EdaData>("/eda"), |
| }; |
|
|