from fastapi import FastAPI import os, time, requests app = FastAPI() # === CONFIG === BASE = os.getenv("DB3_BASE_URL", "https://alesamodio-db3-update.hf.space") # receiver base URL CRON_SECRET = os.getenv("CRON_SECRET", "") # receiver's /run secret (set in receiver Space) HF_TOKEN = os.getenv("HF_TOKEN", "") # READ token if receiver is PRIVATE (set in this trigger Space) # Timings (tune if needed; can also override via query params) DEFAULT_WARMUP_WAIT_S = int(os.getenv("WARMUP_WAIT_S", "180")) # quiet wait after first wake READY_INTERVAL_S = int(os.getenv("READY_INTERVAL_S", "20")) # poll cadence READY_MAX_ATTEMPTS = int(os.getenv("READY_MAX_ATTEMPTS", "15")) # ~5 min total def _headers(): return {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {} @app.get("/") def home(): return {"status": "running", "message": "HF trigger alive"} @app.get("/run") def run(): """ Wake receiver once -> quiet wait -> poll /ready -> fire /run?secret=... Optional query overrides: warmup_wait (int seconds), max_attempts, interval """ log = [] warmup_wait = int(os.getenv("WARMUP_WAIT_S", str(DEFAULT_WARMUP_WAIT_S))) # allow query overrides without importing FastAPI Request try: import starlette.requests # no-op; just to avoid extra dependency notes except Exception: pass # simple query parsing without typing Request from fastapi import Request def get_query(): # tiny helper because we didn't annotate Request in signature import inspect frame = inspect.currentframe().f_back req = frame.f_locals.get('request') # not available in this simple handler return {} # actual overrides via os env only to keep it minimal max_attempts = READY_MAX_ATTEMPTS interval = READY_INTERVAL_S # --- 1) Wake once (GET /) --- try: r = requests.get(f"{BASE}/", headers=_headers(), timeout=10) log.append(f"wake: {r.status_code}") except Exception as e: log.append(f"wake_err: {e}") # --- 2) Quiet wait to let models load --- time.sleep(warmup_wait) log.append(f"quiet_wait_done: {warmup_wait}s") # --- 3) Poll /ready --- ready = False last_text = "" for i in range(1, max_attempts + 1): try: rr = requests.get(f"{BASE}/ready", headers=_headers(), timeout=10) last_text = rr.text if rr.ok and '"ready"' in rr.text: log.append(f"ready_yes_at_attempt:{i}") ready = True break else: log.append(f"ready_no_{i}: {rr.status_code} {rr.text[:80]}") except Exception as e: log.append(f"ready_err_{i}: {e}") time.sleep(interval) if not ready: return { "status": "not_ready", "last_ready_response": last_text[:200], "log": log } # --- 4) Fire /run with secret --- try: run_url = f"{BASE}/run?secret={CRON_SECRET}" final = requests.get(run_url, headers=_headers(), timeout=600) return { "status": "ok", "code": final.status_code, "body": (final.text[:2000] if final.text else ""), "log": log } except Exception as e: return {"status": "run_error", "error": str(e), "log": log}