Spaces:
Sleeping
Sleeping
Adjusting self ping for datfid master for not to sleep
Browse files
main.py
CHANGED
|
@@ -11,6 +11,8 @@ UPSTREAM_URL = os.environ.get("hf_url", "").rstrip("/") # url to acc
|
|
| 11 |
HF_TOKEN = os.environ.get("hf_token") # HF access token to access Private Space
|
| 12 |
DEMO_FORWARD_URL = os.getenv("DEMO_FORWARD_URL", "").rstrip("/") # url to acces demo space
|
| 13 |
DATFID_DEMO_TOKEN = os.getenv("DATFID_DEMO_TOKEN", "") # token to access demo space
|
|
|
|
|
|
|
| 14 |
|
| 15 |
if not HF_TOKEN:
|
| 16 |
raise RuntimeError("Missing secret 'hf_token' in public Space.")
|
|
@@ -39,8 +41,9 @@ SDK_MAX_BODY_BYTES_extended = int(os.getenv("SDK_MAX_BODY_BYTES_extended", "1250
|
|
| 39 |
|
| 40 |
# How long to wait for upstream (API) response; long runs may need 30+ min (1800+)
|
| 41 |
UPSTREAM_TIMEOUT = float(os.getenv("UPSTREAM_TIMEOUT", "900")) # 15 minutes
|
| 42 |
-
# While waiting for a response
|
| 43 |
PING_UPSTREAM_INTERVAL = float(os.getenv("PING_UPSTREAM_INTERVAL", "270"))
|
|
|
|
| 44 |
|
| 45 |
# Early global body-size guard (runs before routes)
|
| 46 |
@app.middleware("http")
|
|
@@ -79,25 +82,31 @@ def _filter_resp_headers(headers: dict) -> dict:
|
|
| 79 |
allowed = {"content-disposition"}
|
| 80 |
return {k: v for k, v in headers.items() if k.lower() in allowed}
|
| 81 |
|
| 82 |
-
async def _ping_upstream_loop(ping_url: str, headers: dict, interval: float):
|
| 83 |
-
"""
|
| 84 |
if interval <= 0:
|
| 85 |
return
|
| 86 |
while True:
|
| 87 |
await asyncio.sleep(interval)
|
| 88 |
try:
|
| 89 |
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
await client.get(ping_url, headers=headers)
|
| 91 |
except asyncio.CancelledError:
|
| 92 |
break
|
| 93 |
except Exception:
|
| 94 |
-
pass
|
| 95 |
|
| 96 |
-
def _start_ping_task(ping_url: str, headers: dict):
|
| 97 |
-
"""Start
|
| 98 |
-
if
|
|
|
|
| 99 |
return None
|
| 100 |
-
return asyncio.create_task(_ping_upstream_loop(ping_url, headers,
|
| 101 |
|
| 102 |
async def _cancel_ping_task(task: asyncio.Task | None):
|
| 103 |
if task is None:
|
|
@@ -133,8 +142,8 @@ async def _forward(path: str, method: str = "GET", json_body=None, user_token: s
|
|
| 133 |
if user_token:
|
| 134 |
headers["X-API-Key"] = user_token
|
| 135 |
|
| 136 |
-
# While waiting
|
| 137 |
-
ping_url = f"{UPSTREAM_URL}secure-ping/"
|
| 138 |
ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 139 |
if user_token:
|
| 140 |
ping_headers["X-API-Key"] = user_token
|
|
@@ -149,12 +158,13 @@ async def _forward(path: str, method: str = "GET", json_body=None, user_token: s
|
|
| 149 |
|
| 150 |
request_task = asyncio.create_task(do_request())
|
| 151 |
interval = PING_UPSTREAM_INTERVAL if PING_UPSTREAM_INTERVAL > 0 else 0.0
|
|
|
|
| 152 |
try:
|
| 153 |
while not request_task.done():
|
| 154 |
-
if
|
| 155 |
await request_task
|
| 156 |
break
|
| 157 |
-
ping_sleep = asyncio.create_task(asyncio.sleep(
|
| 158 |
done, pending = await asyncio.wait(
|
| 159 |
{request_task, ping_sleep},
|
| 160 |
return_when=asyncio.FIRST_COMPLETED,
|
|
@@ -169,11 +179,16 @@ async def _forward(path: str, method: str = "GET", json_body=None, user_token: s
|
|
| 169 |
pass
|
| 170 |
if request_task in done:
|
| 171 |
break
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
if not request_task.done():
|
| 178 |
request_task.cancel()
|
| 179 |
try:
|
|
@@ -216,7 +231,7 @@ async def _forward_stream(path: str, files=None, data=None, user_token: str | No
|
|
| 216 |
ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 217 |
if user_token:
|
| 218 |
ping_headers["X-API-Key"] = user_token
|
| 219 |
-
ping_task = _start_ping_task(f"{UPSTREAM_URL}secure-ping/", ping_headers)
|
| 220 |
timeout = httpx.Timeout(UPSTREAM_TIMEOUT)
|
| 221 |
client = httpx.AsyncClient(timeout=timeout, follow_redirects=True)
|
| 222 |
|
|
@@ -280,7 +295,7 @@ async def _forward_stream(path: str, files=None, data=None, user_token: str | No
|
|
| 280 |
|
| 281 |
|
| 282 |
async def _forward_multipart_json(path: str, files=None, data=None, user_token: str | None = None, method: str = "POST"):
|
| 283 |
-
"""POST multipart to upstream and return JSON.
|
| 284 |
url = f"{UPSTREAM_URL}{path}"
|
| 285 |
headers = {
|
| 286 |
"Authorization": f"Bearer {HF_TOKEN}",
|
|
@@ -288,7 +303,7 @@ async def _forward_multipart_json(path: str, files=None, data=None, user_token:
|
|
| 288 |
}
|
| 289 |
if user_token:
|
| 290 |
headers["X-API-Key"] = user_token
|
| 291 |
-
ping_url = f"{UPSTREAM_URL}secure-ping/"
|
| 292 |
ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 293 |
if user_token:
|
| 294 |
ping_headers["X-API-Key"] = user_token
|
|
@@ -302,12 +317,13 @@ async def _forward_multipart_json(path: str, files=None, data=None, user_token:
|
|
| 302 |
|
| 303 |
request_task = asyncio.create_task(do_request())
|
| 304 |
interval = PING_UPSTREAM_INTERVAL if PING_UPSTREAM_INTERVAL > 0 else 0.0
|
|
|
|
| 305 |
try:
|
| 306 |
while not request_task.done():
|
| 307 |
-
if
|
| 308 |
await request_task
|
| 309 |
break
|
| 310 |
-
ping_sleep = asyncio.create_task(asyncio.sleep(
|
| 311 |
done, pending = await asyncio.wait(
|
| 312 |
{request_task, ping_sleep},
|
| 313 |
return_when=asyncio.FIRST_COMPLETED,
|
|
@@ -322,11 +338,16 @@ async def _forward_multipart_json(path: str, files=None, data=None, user_token:
|
|
| 322 |
pass
|
| 323 |
if request_task in done:
|
| 324 |
break
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
if not request_task.done():
|
| 331 |
request_task.cancel()
|
| 332 |
try:
|
|
|
|
| 11 |
HF_TOKEN = os.environ.get("hf_token") # HF access token to access Private Space
|
| 12 |
DEMO_FORWARD_URL = os.getenv("DEMO_FORWARD_URL", "").rstrip("/") # url to acces demo space
|
| 13 |
DATFID_DEMO_TOKEN = os.getenv("DATFID_DEMO_TOKEN", "") # token to access demo space
|
| 14 |
+
# This Space's public URL (used to ping self while waiting so HF does not put this Space to sleep). Override with SELF_URL env if different.
|
| 15 |
+
SELF_URL = os.getenv("SELF_URL", "https://datfid-org-datfid-master.hf.space").rstrip("/")
|
| 16 |
|
| 17 |
if not HF_TOKEN:
|
| 18 |
raise RuntimeError("Missing secret 'hf_token' in public Space.")
|
|
|
|
| 41 |
|
| 42 |
# How long to wait for upstream (API) response; long runs may need 30+ min (1800+)
|
| 43 |
UPSTREAM_TIMEOUT = float(os.getenv("UPSTREAM_TIMEOUT", "900")) # 15 minutes
|
| 44 |
+
# While waiting for a response: send secure_ping to upstream every N seconds; if SELF_URL set, also ping self every SELF_PING_INTERVAL so this Space stays awake
|
| 45 |
PING_UPSTREAM_INTERVAL = float(os.getenv("PING_UPSTREAM_INTERVAL", "270"))
|
| 46 |
+
SELF_PING_INTERVAL = float(os.getenv("SELF_PING_INTERVAL", "120"))
|
| 47 |
|
| 48 |
# Early global body-size guard (runs before routes)
|
| 49 |
@app.middleware("http")
|
|
|
|
| 82 |
allowed = {"content-disposition"}
|
| 83 |
return {k: v for k, v in headers.items() if k.lower() in allowed}
|
| 84 |
|
| 85 |
+
async def _ping_upstream_loop(ping_url: str, headers: dict, interval: float, self_url: str = ""):
|
| 86 |
+
"""Every `interval` seconds: if self_url set, GET self_url/keep-alive (keep this Space awake), then GET ping_url (backend). Stops when cancelled."""
|
| 87 |
if interval <= 0:
|
| 88 |
return
|
| 89 |
while True:
|
| 90 |
await asyncio.sleep(interval)
|
| 91 |
try:
|
| 92 |
async with httpx.AsyncClient(timeout=10.0) as client:
|
| 93 |
+
if self_url:
|
| 94 |
+
try:
|
| 95 |
+
await client.get(f"{self_url}/keep-alive")
|
| 96 |
+
except Exception:
|
| 97 |
+
pass
|
| 98 |
await client.get(ping_url, headers=headers)
|
| 99 |
except asyncio.CancelledError:
|
| 100 |
break
|
| 101 |
except Exception:
|
| 102 |
+
pass
|
| 103 |
|
| 104 |
+
def _start_ping_task(ping_url: str, headers: dict, self_url: str = ""):
|
| 105 |
+
"""Start background ping task. If SELF_URL set, uses SELF_PING_INTERVAL and pings self + backend; else PING_UPSTREAM_INTERVAL, backend only."""
|
| 106 |
+
interval = (SELF_PING_INTERVAL if self_url and SELF_PING_INTERVAL > 0 else PING_UPSTREAM_INTERVAL) or PING_UPSTREAM_INTERVAL
|
| 107 |
+
if interval <= 0:
|
| 108 |
return None
|
| 109 |
+
return asyncio.create_task(_ping_upstream_loop(ping_url, headers, interval, self_url))
|
| 110 |
|
| 111 |
async def _cancel_ping_task(task: asyncio.Task | None):
|
| 112 |
if task is None:
|
|
|
|
| 142 |
if user_token:
|
| 143 |
headers["X-API-Key"] = user_token
|
| 144 |
|
| 145 |
+
# While waiting: ping backend (secure_ping) and, if SELF_URL set, ping self so this Space stays awake
|
| 146 |
+
ping_url = f"{UPSTREAM_URL}/secure-ping/"
|
| 147 |
ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 148 |
if user_token:
|
| 149 |
ping_headers["X-API-Key"] = user_token
|
|
|
|
| 158 |
|
| 159 |
request_task = asyncio.create_task(do_request())
|
| 160 |
interval = PING_UPSTREAM_INTERVAL if PING_UPSTREAM_INTERVAL > 0 else 0.0
|
| 161 |
+
wait_sec = (SELF_PING_INTERVAL if SELF_URL and SELF_PING_INTERVAL > 0 else interval) or interval
|
| 162 |
try:
|
| 163 |
while not request_task.done():
|
| 164 |
+
if wait_sec <= 0:
|
| 165 |
await request_task
|
| 166 |
break
|
| 167 |
+
ping_sleep = asyncio.create_task(asyncio.sleep(wait_sec))
|
| 168 |
done, pending = await asyncio.wait(
|
| 169 |
{request_task, ping_sleep},
|
| 170 |
return_when=asyncio.FIRST_COMPLETED,
|
|
|
|
| 179 |
pass
|
| 180 |
if request_task in done:
|
| 181 |
break
|
| 182 |
+
async with httpx.AsyncClient(timeout=10.0) as c:
|
| 183 |
+
if SELF_URL:
|
| 184 |
+
try:
|
| 185 |
+
await c.get(f"{SELF_URL}/keep-alive")
|
| 186 |
+
except Exception:
|
| 187 |
+
pass
|
| 188 |
+
try:
|
| 189 |
+
await c.get(ping_url, headers=ping_headers)
|
| 190 |
+
except Exception:
|
| 191 |
+
pass
|
| 192 |
if not request_task.done():
|
| 193 |
request_task.cancel()
|
| 194 |
try:
|
|
|
|
| 231 |
ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 232 |
if user_token:
|
| 233 |
ping_headers["X-API-Key"] = user_token
|
| 234 |
+
ping_task = _start_ping_task(f"{UPSTREAM_URL}/secure-ping/", ping_headers, SELF_URL)
|
| 235 |
timeout = httpx.Timeout(UPSTREAM_TIMEOUT)
|
| 236 |
client = httpx.AsyncClient(timeout=timeout, follow_redirects=True)
|
| 237 |
|
|
|
|
| 295 |
|
| 296 |
|
| 297 |
async def _forward_multipart_json(path: str, files=None, data=None, user_token: str | None = None, method: str = "POST"):
|
| 298 |
+
"""POST multipart to upstream and return JSON. While waiting: secure_ping to backend; if SELF_URL set, ping self."""
|
| 299 |
url = f"{UPSTREAM_URL}{path}"
|
| 300 |
headers = {
|
| 301 |
"Authorization": f"Bearer {HF_TOKEN}",
|
|
|
|
| 303 |
}
|
| 304 |
if user_token:
|
| 305 |
headers["X-API-Key"] = user_token
|
| 306 |
+
ping_url = f"{UPSTREAM_URL}/secure-ping/"
|
| 307 |
ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 308 |
if user_token:
|
| 309 |
ping_headers["X-API-Key"] = user_token
|
|
|
|
| 317 |
|
| 318 |
request_task = asyncio.create_task(do_request())
|
| 319 |
interval = PING_UPSTREAM_INTERVAL if PING_UPSTREAM_INTERVAL > 0 else 0.0
|
| 320 |
+
wait_sec = (SELF_PING_INTERVAL if SELF_URL and SELF_PING_INTERVAL > 0 else interval) or interval
|
| 321 |
try:
|
| 322 |
while not request_task.done():
|
| 323 |
+
if wait_sec <= 0:
|
| 324 |
await request_task
|
| 325 |
break
|
| 326 |
+
ping_sleep = asyncio.create_task(asyncio.sleep(wait_sec))
|
| 327 |
done, pending = await asyncio.wait(
|
| 328 |
{request_task, ping_sleep},
|
| 329 |
return_when=asyncio.FIRST_COMPLETED,
|
|
|
|
| 338 |
pass
|
| 339 |
if request_task in done:
|
| 340 |
break
|
| 341 |
+
async with httpx.AsyncClient(timeout=10.0) as c:
|
| 342 |
+
if SELF_URL:
|
| 343 |
+
try:
|
| 344 |
+
await c.get(f"{SELF_URL}/keep-alive")
|
| 345 |
+
except Exception:
|
| 346 |
+
pass
|
| 347 |
+
try:
|
| 348 |
+
await c.get(ping_url, headers=ping_headers)
|
| 349 |
+
except Exception:
|
| 350 |
+
pass
|
| 351 |
if not request_task.done():
|
| 352 |
request_task.cancel()
|
| 353 |
try:
|