const POTABILITY_API = process.env.POTABILITY_API || 'https://dpv007-newclear.hf.space/gradio_api/call/predict_potability'; /** * Intelligent Water Quality Index heuristic fallback * Evaluates standard WHO water quality thresholds */ export function calculateFallbackPotability( ph: number, hardness: number, turbidity: number, temperature: number ): number { // pH safe range: 6.5 - 8.5 let phScore = 1.0; if (ph < 6.5) phScore = Math.max(0, 1 - (6.5 - ph) / 2.5); else if (ph > 8.5) phScore = Math.max(0, 1 - (ph - 8.5) / 2.5); // Turbidity ideal < 5 NTU, acceptable up to 10 NTU let turbidityScore = 1.0; if (turbidity > 5) turbidityScore = Math.max(0, 1 - (turbidity - 5) / 10); // Hardness ideal 60 - 200 mg/L let hardnessScore = 1.0; if (hardness < 60) hardnessScore = Math.max(0.2, 1 - (60 - hardness) / 100); else if (hardness > 200) hardnessScore = Math.max(0.1, 1 - (hardness - 200) / 300); // Temperature ideal 10 - 25 °C let tempScore = 1.0; if (temperature < 10) tempScore = Math.max(0.4, 1 - (10 - temperature) / 20); else if (temperature > 25) tempScore = Math.max(0.2, 1 - (temperature - 25) / 30); const potability = (phScore * 0.35) + (turbidityScore * 0.35) + (hardnessScore * 0.2) + (tempScore * 0.1); return Number(Math.max(0.01, Math.min(0.99, potability)).toFixed(4)); } /** * Predict potability using Hugging Face Gradio ML Model with fallback */ export async function predictPotability( ph: number, hardness: number, turbidity: number, temperature: number ): Promise { try { const postRes = await fetch(POTABILITY_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data: [ph, hardness, turbidity, temperature] }), signal: AbortSignal.timeout(5000), }); if (!postRes.ok) { return calculateFallbackPotability(ph, hardness, turbidity, temperature); } const { event_id } = await postRes.json(); if (!event_id) { return calculateFallbackPotability(ph, hardness, turbidity, temperature); } const getRes = await fetch(`${POTABILITY_API}/${event_id}`, { method: 'GET', signal: AbortSignal.timeout(6000), }); if (!getRes.ok) { return calculateFallbackPotability(ph, hardness, turbidity, temperature); } const text = await getRes.text(); const lines = text.trim().split('\n'); for (const line of lines) { if (!line.startsWith('data:')) continue; const jsonStr = line.replace('data:', '').trim(); if (!jsonStr) continue; try { const json = JSON.parse(jsonStr); if (Array.isArray(json) && json.length > 0) { const matchProb = json.map((s: any) => { if (typeof s !== 'string') return null; const match = s.match(/(?:^|[^\w-])Potable:\s*([0-9.]+)/i); return match ? parseFloat(match[1]) : null; }).filter((v: number | null): v is number => v !== null && !isNaN(v)); if (matchProb.length > 0) { const avg = matchProb.reduce((a, b) => a + b, 0) / matchProb.length; return Number(avg.toFixed(4)); } } } catch { // ignore parse errors } } return calculateFallbackPotability(ph, hardness, turbidity, temperature); } catch (err) { return calculateFallbackPotability(ph, hardness, turbidity, temperature); } }