HydroSense / lib /potability.ts
dpv007's picture
Update chat model to qwen/qwen3.8-27b and fix ML potability inference fallback
037f190
Raw History Blame Contribute Delete
3.43 kB
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<number> {
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);
}
}