"""End-to-end performance audit — every chart, every API, cold + warm. Goal: find anything that takes >5 seconds (Chiku's life depends on responsive tools during a medical event). Reports three tiers: FAIL > 5000 ms (unacceptable — must fix) WARN > 1500 ms (noticeable lag, optimize if possible) OK <= 1500 ms (responsive) Run: python -m scripts.perf_audit """ from __future__ import annotations import json import logging import sys import time from pathlib import Path _ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(_ROOT)) from dotenv import load_dotenv load_dotenv(_ROOT / ".env") logging.basicConfig(level=logging.WARNING, format="%(levelname)s: %(message)s") CHIKU = "ec8ed3df-3b7b-4013-8d6d-c76832e1ec88" FAIL_MS = 5000 WARN_MS = 1500 class Row: __slots__ = ("label", "ms") def __init__(self, label: str, ms: float): self.label = label self.ms = ms def tier(self) -> str: if self.ms > FAIL_MS: return "FAIL" if self.ms > WARN_MS: return "WARN" return "OK " def timeit(label: str, fn) -> Row: t0 = time.perf_counter() fn() ms = (time.perf_counter() - t0) * 1000 return Row(label, ms) def audit_charts_cold() -> list[Row]: """Cold-start: time each chart in a fresh Python session by design. Call from a brand-new subprocess to measure realistic first-call cost. """ from guardiantails.services.hf_sync import download_backup from guardiantails.models import init_db, get_db download_backup() init_db() rows: list[Row] = [] # Import and run each chart from cold — measures full first-call cost # including any module chains loaded lazily from guardiantails.core.charts import ( dashboard_timeline, walk_distance_chart, weight_timeline, medication_adherence_trend, dose_status_calendar, seizure_severity_bar, seizure_by_hour_bar, seizure_triggers_pie, seizure_moon_polar, seizure_weather_chart, seizure_vs_pressure_scatter, poop_timing_bar, lab_trends_chart, cost_breakdown_pie, cost_cumulative_line, ) from guardiantails.core.dashboard_tab import _get_pet_photo_html CHART_FNS = [ ("dashboard_timeline 90d", lambda: dashboard_timeline(CHIKU, days=90)), ("walk_distance_chart 90d", lambda: walk_distance_chart(CHIKU, days=90)), ("weight_timeline 90d", lambda: weight_timeline(CHIKU, days=90)), ("medication_adherence", lambda: medication_adherence_trend(CHIKU)), ("dose_status_calendar", lambda: dose_status_calendar(CHIKU)), ("seizure_severity_bar", lambda: seizure_severity_bar(CHIKU)), ("seizure_by_hour_bar", lambda: seizure_by_hour_bar(CHIKU)), ("seizure_triggers_pie", lambda: seizure_triggers_pie(CHIKU)), ("seizure_moon_polar", lambda: seizure_moon_polar(CHIKU)), ("seizure_weather_chart", lambda: seizure_weather_chart(CHIKU)), ("seizure_vs_pressure", lambda: seizure_vs_pressure_scatter(CHIKU)), ("poop_timing_bar", lambda: poop_timing_bar(CHIKU)), ("lab_trends_chart", lambda: lab_trends_chart(CHIKU)), ("cost_breakdown_pie", lambda: cost_breakdown_pie(CHIKU)), ("cost_cumulative_line", lambda: cost_cumulative_line(CHIKU)), ("pet_photo_html", lambda: _get_pet_photo_html(CHIKU)), ] for label, fn in CHART_FNS: rows.append(timeit(label, fn)) return rows def audit_api_endpoints() -> list[Row]: """Hit the live Space's API endpoints, time each round-trip (cold once, warm twice), and report the WARM time (simulates steady-state usage). """ rows: list[Row] = [] try: from gradio_client import Client except Exception as e: rows.append(Row(f"gradio_client import failed: {e}", 999999)) return rows c = Client("kshitijthakkar/guardiantails", auth=("admin", "neoThakkar@2017")) endpoints = [ ("/v1/health", lambda: c.predict(api_name="/v1/health")), ("/v1/get_dashboard_stats", lambda: c.predict(CHIKU, api_name="/v1/get_dashboard_stats")), ("/v1/get_daily_log (recent)",lambda: c.predict(CHIKU, "2026-04-16", api_name="/v1/get_daily_log")), ("/v1/get_daily_log (old)", lambda: c.predict(CHIKU, "2025-12-20", api_name="/v1/get_daily_log")), ("/v1/get_seizures", lambda: c.predict(CHIKU, "10", "0", api_name="/v1/get_seizures")), ("/v1/get_weight_history", lambda: c.predict(CHIKU, api_name="/v1/get_weight_history")), ("/v1/get_emergency_packet", lambda: c.predict(CHIKU, api_name="/v1/get_emergency_packet")), ] for label, fn in endpoints: # First call = cold; time the second one (warm) so we report realistic steady-state try: fn() # warm the path except Exception as e: rows.append(Row(f"{label} (ERROR: {str(e)[:80]})", 999999)) continue rows.append(timeit(label, fn)) return rows def print_table(title: str, rows: list[Row]) -> None: print() print("=" * 70) print(title) print("=" * 70) print(f"{'TIER':6}{'MS':>8} LABEL") print("-" * 70) # Sort by ms desc to show worst first for r in sorted(rows, key=lambda r: -r.ms): print(f"{r.tier():6}{r.ms:7.0f} {r.label}") fails = [r for r in rows if r.ms > FAIL_MS] warns = [r for r in rows if WARN_MS < r.ms <= FAIL_MS] print() print(f" {len(fails)} FAIL | {len(warns)} WARN | {len(rows)-len(fails)-len(warns)} OK") def main() -> None: print("AUDIT: charts (local, cold-start)") chart_rows = audit_charts_cold() print_table("CHARTS — local cold-start", chart_rows) print() print("AUDIT: live Space API (warm — 2nd call)") api_rows = audit_api_endpoints() print_table("API — live Space round-trip (warm)", api_rows) if __name__ == "__main__": main()