/** * useAIHealthCheck.ts — Hook per testare la latenza dei provider AI via backend * * Chiama GET /api/ai/health (FastAPI HF Space) che esegue probe parallelo * su tutti i provider configurati (openrouter, gemini, groq, hf). * Cache in-memory 60s lato client — non chiama il backend ad ogni render. * * Usage: * const { data, loading, error, refresh } = useAIHealthCheck(); * * Distinto da useProviderHealth (Sessione 8) che monitora il runtime locale. */ import { useState, useCallback, useRef } from "react"; import { makeTimedSignal } from "@/lib/agentLoop/networkConstants"; // Loop-12: iOS-safe const _BACKEND = (import.meta.env.VITE_BACKEND_URL ?? "").replace(/\/$/, ""); const CACHE_TTL_MS = 60_000; export interface AIProviderResult { name: string; ok: boolean; latency_ms: number; model: string; error?: string; } export interface AIHealthData { providers: AIProviderResult[]; tested_at: number; } export function useAIHealthCheck() { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const cacheRef = useRef<{ data: AIHealthData; at: number } | null>(null); const refresh = useCallback(async (force = false) => { if (!_BACKEND) { setError("VITE_BACKEND_URL non configurato"); return; } // Cache hit (skip se force=true) if (!force && cacheRef.current && Date.now() - cacheRef.current.at < CACHE_TTL_MS) { setData(cacheRef.current.data); return; } setLoading(true); setError(null); try { const res = await fetch(`${_BACKEND}/api/ai/health`, { signal: makeTimedSignal(15_000), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = (await res.json()) as AIHealthData; cacheRef.current = { data: json, at: Date.now() }; setData(json); } catch (err: unknown) { setError((err as Error).message ?? "Errore sconosciuto"); } finally { setLoading(false); } }, []); return { data, loading, error, refresh }; }