DrValera commited on
Commit
24de318
·
verified ·
1 Parent(s): 06298fc

Adjustment non-sleep of datfid master space while datfid api is still working

Browse files
Files changed (1) hide show
  1. main.py +112 -12
main.py CHANGED
@@ -38,7 +38,7 @@ SDK_MAX_BODY_BYTES = int(os.getenv("SDK_MAX_BODY_BYTES", "25000000")) # 25MB de
38
  SDK_MAX_BODY_BYTES_extended = int(os.getenv("SDK_MAX_BODY_BYTES_extended", "125000000")) # 125MB default for prod
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, ping upstream every N seconds so the backend Space does not sleep
43
  PING_UPSTREAM_INTERVAL = float(os.getenv("PING_UPSTREAM_INTERVAL", "270"))
44
 
@@ -124,6 +124,7 @@ async def _forward(path: str, method: str = "GET", json_body=None, user_token: s
124
  - 'Authorization: Bearer <HF_TOKEN>' to pass HF private gate
125
  - 'X-API-Key: <dt+...>' so your private app can validate the user token
126
  While waiting, pings upstream every PING_UPSTREAM_INTERVAL seconds so the backend Space does not sleep.
 
127
  """
128
  url = f"{UPSTREAM_URL}{path}"
129
  headers = {
@@ -133,13 +134,63 @@ 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
- ping_task = _start_ping_task(f"{UPSTREAM_URL}/", {"Authorization": f"Bearer {HF_TOKEN}"})
137
- try:
138
- timeout = httpx.Timeout(UPSTREAM_TIMEOUT)
 
 
 
 
139
  async with httpx.AsyncClient(timeout=timeout) as client:
140
  r = await client.request(method, url, headers=headers, json=json_body)
141
- finally:
142
- await _cancel_ping_task(ping_task)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  ct = r.headers.get("content-type", "")
145
 
@@ -224,7 +275,7 @@ async def _forward_stream(path: str, files=None, data=None, user_token: str | No
224
 
225
 
226
  async def _forward_multipart_json(path: str, files=None, data=None, user_token: str | None = None, method: str = "POST"):
227
- """POST multipart (files + data) to upstream and return JSON response. Pings upstream every PING_UPSTREAM_INTERVAL while waiting."""
228
  url = f"{UPSTREAM_URL}{path}"
229
  headers = {
230
  "Authorization": f"Bearer {HF_TOKEN}",
@@ -232,13 +283,62 @@ async def _forward_multipart_json(path: str, files=None, data=None, user_token:
232
  }
233
  if user_token:
234
  headers["X-API-Key"] = user_token
235
- ping_task = _start_ping_task(f"{UPSTREAM_URL}/", {"Authorization": f"Bearer {HF_TOKEN}"})
236
- try:
237
- timeout = httpx.Timeout(UPSTREAM_TIMEOUT)
 
 
 
 
238
  async with httpx.AsyncClient(timeout=timeout) as client:
239
  r = await client.request(method, url, headers=headers, files=files, data=data)
240
- finally:
241
- await _cancel_ping_task(ping_task)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  ct = r.headers.get("content-type", "")
243
  if "application/json" in ct:
244
  try:
 
38
  SDK_MAX_BODY_BYTES_extended = int(os.getenv("SDK_MAX_BODY_BYTES_extended", "125000000")) # 125MB default for prod
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, ping upstream every N seconds so the backend Space does not sleep
43
  PING_UPSTREAM_INTERVAL = float(os.getenv("PING_UPSTREAM_INTERVAL", "270"))
44
 
 
124
  - 'Authorization: Bearer <HF_TOKEN>' to pass HF private gate
125
  - 'X-API-Key: <dt+...>' so your private app can validate the user token
126
  While waiting, pings upstream every PING_UPSTREAM_INTERVAL seconds so the backend Space does not sleep.
127
+ Uses explicit wait (response OR ping timer) so pings run even when the event loop doesn't preempt the request.
128
  """
129
  url = f"{UPSTREAM_URL}{path}"
130
  headers = {
 
134
  if user_token:
135
  headers["X-API-Key"] = user_token
136
 
137
+ ping_url = f"{UPSTREAM_URL}/"
138
+ ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
139
+ timeout = httpx.Timeout(UPSTREAM_TIMEOUT)
140
+ r = None
141
+
142
+ async def do_request():
143
+ nonlocal r
144
  async with httpx.AsyncClient(timeout=timeout) as client:
145
  r = await client.request(method, url, headers=headers, json=json_body)
146
+
147
+ request_task = asyncio.create_task(do_request())
148
+ interval = PING_UPSTREAM_INTERVAL if PING_UPSTREAM_INTERVAL > 0 else 0.0
149
+ try:
150
+ while not request_task.done():
151
+ if interval <= 0:
152
+ await request_task
153
+ break
154
+ ping_sleep = asyncio.create_task(asyncio.sleep(interval))
155
+ done, pending = await asyncio.wait(
156
+ {request_task, ping_sleep},
157
+ return_when=asyncio.FIRST_COMPLETED,
158
+ timeout=UPSTREAM_TIMEOUT + 10,
159
+ )
160
+ for t in pending:
161
+ if t is not request_task:
162
+ t.cancel()
163
+ try:
164
+ await t
165
+ except asyncio.CancelledError:
166
+ pass
167
+ if request_task in done:
168
+ break
169
+ # Ping timer fired: hit upstream so backend does not sleep
170
+ try:
171
+ async with httpx.AsyncClient(timeout=10.0) as client:
172
+ await client.get(ping_url, headers=ping_headers)
173
+ except Exception:
174
+ pass
175
+ if not request_task.done():
176
+ request_task.cancel()
177
+ try:
178
+ await request_task
179
+ except asyncio.CancelledError:
180
+ pass
181
+ await request_task
182
+ except asyncio.CancelledError:
183
+ request_task.cancel()
184
+ try:
185
+ await request_task
186
+ except asyncio.CancelledError:
187
+ pass
188
+ raise
189
+ exc = request_task.exception()
190
+ if exc is not None:
191
+ raise exc
192
+ if r is None:
193
+ raise RuntimeError("Upstream request did not complete")
194
 
195
  ct = r.headers.get("content-type", "")
196
 
 
275
 
276
 
277
  async def _forward_multipart_json(path: str, files=None, data=None, user_token: str | None = None, method: str = "POST"):
278
+ """POST multipart (files + data) to upstream and return JSON. Explicit ping-while-wait (same as _forward)."""
279
  url = f"{UPSTREAM_URL}{path}"
280
  headers = {
281
  "Authorization": f"Bearer {HF_TOKEN}",
 
283
  }
284
  if user_token:
285
  headers["X-API-Key"] = user_token
286
+ ping_url = f"{UPSTREAM_URL}/"
287
+ ping_headers = {"Authorization": f"Bearer {HF_TOKEN}"}
288
+ timeout = httpx.Timeout(UPSTREAM_TIMEOUT)
289
+ r = None
290
+
291
+ async def do_request():
292
+ nonlocal r
293
  async with httpx.AsyncClient(timeout=timeout) as client:
294
  r = await client.request(method, url, headers=headers, files=files, data=data)
295
+
296
+ request_task = asyncio.create_task(do_request())
297
+ interval = PING_UPSTREAM_INTERVAL if PING_UPSTREAM_INTERVAL > 0 else 0.0
298
+ try:
299
+ while not request_task.done():
300
+ if interval <= 0:
301
+ await request_task
302
+ break
303
+ ping_sleep = asyncio.create_task(asyncio.sleep(interval))
304
+ done, pending = await asyncio.wait(
305
+ {request_task, ping_sleep},
306
+ return_when=asyncio.FIRST_COMPLETED,
307
+ timeout=UPSTREAM_TIMEOUT + 10,
308
+ )
309
+ for t in pending:
310
+ if t is not request_task:
311
+ t.cancel()
312
+ try:
313
+ await t
314
+ except asyncio.CancelledError:
315
+ pass
316
+ if request_task in done:
317
+ break
318
+ try:
319
+ async with httpx.AsyncClient(timeout=10.0) as client:
320
+ await client.get(ping_url, headers=ping_headers)
321
+ except Exception:
322
+ pass
323
+ if not request_task.done():
324
+ request_task.cancel()
325
+ try:
326
+ await request_task
327
+ except asyncio.CancelledError:
328
+ pass
329
+ await request_task
330
+ except asyncio.CancelledError:
331
+ request_task.cancel()
332
+ try:
333
+ await request_task
334
+ except asyncio.CancelledError:
335
+ pass
336
+ raise
337
+ exc = request_task.exception()
338
+ if exc is not None:
339
+ raise exc
340
+ if r is None:
341
+ raise RuntimeError("Upstream request did not complete")
342
  ct = r.headers.get("content-type", "")
343
  if "application/json" in ct:
344
  try: