Fix build error
Browse files
main.py
CHANGED
|
@@ -22,14 +22,15 @@ HF_TOKEN = os.getenv("HF_TOKEN")
|
|
| 22 |
DEV_URL = "https://PPAL-SongLab-UGA-fruit-analyzer-dev.hf.space"
|
| 23 |
PROD_URL = "https://PPAL-SongLab-UGA-fruit-analyzer.hf.space"
|
| 24 |
HF_RATE_LIMIT_STATUS = 429
|
|
|
|
| 25 |
|
| 26 |
|
| 27 |
-
def
|
| 28 |
last_response = None
|
| 29 |
for attempt in range(max_retries + 1):
|
| 30 |
response = method(url, **kwargs)
|
| 31 |
last_response = response
|
| 32 |
-
if response.status_code
|
| 33 |
return response
|
| 34 |
|
| 35 |
retry_after = response.headers.get("retry-after")
|
|
@@ -38,7 +39,7 @@ def request_with_429_backoff(method, url, *, max_retries=3, **kwargs):
|
|
| 38 |
except ValueError:
|
| 39 |
wait_s = None
|
| 40 |
if wait_s is None:
|
| 41 |
-
wait_s = [2.0, 5.0, 10.0, 15.0][min(attempt, 3)]
|
| 42 |
if attempt < max_retries:
|
| 43 |
time.sleep(min(max(wait_s, 0.5), 20.0))
|
| 44 |
return last_response
|
|
@@ -50,8 +51,28 @@ def hf_error_message(exc):
|
|
| 50 |
retry_after = response.headers.get("retry-after")
|
| 51 |
suffix = f" Retry after about {retry_after} seconds." if retry_after else " Please wait a minute and try again."
|
| 52 |
return f"Hugging Face is rate limiting this Space after several retry attempts.{suffix}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
return str(exc)
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
@app.get("/")
|
| 56 |
def read_root():
|
| 57 |
return {"status": "Render Proxy is awake!"}
|
|
@@ -97,7 +118,7 @@ async def proxy_process(request: Request, file: UploadFile = File(...), password
|
|
| 97 |
|
| 98 |
# FIX: Define the blocking request
|
| 99 |
def make_post():
|
| 100 |
-
return
|
| 101 |
requests.post,
|
| 102 |
target_url,
|
| 103 |
headers=headers,
|
|
@@ -112,7 +133,7 @@ async def proxy_process(request: Request, file: UploadFile = File(...), password
|
|
| 112 |
response.raise_for_status()
|
| 113 |
return response.json()
|
| 114 |
except requests.exceptions.RequestException as e:
|
| 115 |
-
return
|
| 116 |
except Exception as e:
|
| 117 |
return {"success": False, "message": f"Proxy Error (Internal): {str(e)}"}
|
| 118 |
|
|
@@ -135,7 +156,7 @@ async def proxy_compatibility(request: Request, password: str = Form(""), userna
|
|
| 135 |
data.setdefault("username", username)
|
| 136 |
|
| 137 |
def make_post():
|
| 138 |
-
return
|
| 139 |
requests.post,
|
| 140 |
target_url,
|
| 141 |
headers=headers,
|
|
@@ -149,7 +170,7 @@ async def proxy_compatibility(request: Request, password: str = Form(""), userna
|
|
| 149 |
response.raise_for_status()
|
| 150 |
return response.json()
|
| 151 |
except requests.exceptions.RequestException as e:
|
| 152 |
-
return
|
| 153 |
except Exception as e:
|
| 154 |
return {"success": False, "message": f"Proxy Error (Internal): {str(e)}", "results": []}
|
| 155 |
|
|
@@ -162,7 +183,7 @@ async def proxy_batch_stage(payload: dict = Body(...)):
|
|
| 162 |
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 163 |
|
| 164 |
def make_post():
|
| 165 |
-
return
|
| 166 |
requests.post,
|
| 167 |
target_url,
|
| 168 |
headers=headers,
|
|
@@ -175,7 +196,7 @@ async def proxy_batch_stage(payload: dict = Body(...)):
|
|
| 175 |
response.raise_for_status()
|
| 176 |
return response.json()
|
| 177 |
except requests.exceptions.RequestException as e:
|
| 178 |
-
return
|
| 179 |
except Exception as e:
|
| 180 |
return {"success": False, "message": f"Proxy Error (Internal): {str(e)}", "rows": []}
|
| 181 |
|
|
|
|
| 22 |
DEV_URL = "https://PPAL-SongLab-UGA-fruit-analyzer-dev.hf.space"
|
| 23 |
PROD_URL = "https://PPAL-SongLab-UGA-fruit-analyzer.hf.space"
|
| 24 |
HF_RATE_LIMIT_STATUS = 429
|
| 25 |
+
HF_TRANSIENT_STATUS = {429, 502, 503, 504}
|
| 26 |
|
| 27 |
|
| 28 |
+
def request_with_hf_backoff(method, url, *, max_retries=3, **kwargs):
|
| 29 |
last_response = None
|
| 30 |
for attempt in range(max_retries + 1):
|
| 31 |
response = method(url, **kwargs)
|
| 32 |
last_response = response
|
| 33 |
+
if response.status_code not in HF_TRANSIENT_STATUS:
|
| 34 |
return response
|
| 35 |
|
| 36 |
retry_after = response.headers.get("retry-after")
|
|
|
|
| 39 |
except ValueError:
|
| 40 |
wait_s = None
|
| 41 |
if wait_s is None:
|
| 42 |
+
wait_s = [2.0, 5.0, 10.0, 15.0][min(attempt, 3)] if response.status_code == HF_RATE_LIMIT_STATUS else [3.0, 8.0, 15.0, 20.0][min(attempt, 3)]
|
| 43 |
if attempt < max_retries:
|
| 44 |
time.sleep(min(max(wait_s, 0.5), 20.0))
|
| 45 |
return last_response
|
|
|
|
| 51 |
retry_after = response.headers.get("retry-after")
|
| 52 |
suffix = f" Retry after about {retry_after} seconds." if retry_after else " Please wait a minute and try again."
|
| 53 |
return f"Hugging Face is rate limiting this Space after several retry attempts.{suffix}"
|
| 54 |
+
if response is not None and response.status_code in {502, 503, 504}:
|
| 55 |
+
return "Hugging Face Space is temporarily unavailable or waking up after several retry attempts. Please wait a moment and try again."
|
| 56 |
+
if isinstance(exc, requests.exceptions.Timeout):
|
| 57 |
+
return "Hugging Face Space did not respond before the proxy timeout. Please try again."
|
| 58 |
+
if isinstance(exc, requests.exceptions.ConnectionError):
|
| 59 |
+
return "Could not connect to the Hugging Face Space. Please try again."
|
| 60 |
return str(exc)
|
| 61 |
|
| 62 |
+
|
| 63 |
+
def is_retryable_hf_exception(exc):
|
| 64 |
+
response = getattr(exc, "response", None)
|
| 65 |
+
return bool(response is not None and response.status_code in HF_TRANSIENT_STATUS)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def proxy_error_payload(prefix, exc, **extra):
|
| 69 |
+
return {
|
| 70 |
+
"success": False,
|
| 71 |
+
"message": f"{prefix}: {hf_error_message(exc)}",
|
| 72 |
+
"retryable": is_retryable_hf_exception(exc),
|
| 73 |
+
**extra,
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
@app.get("/")
|
| 77 |
def read_root():
|
| 78 |
return {"status": "Render Proxy is awake!"}
|
|
|
|
| 118 |
|
| 119 |
# FIX: Define the blocking request
|
| 120 |
def make_post():
|
| 121 |
+
return request_with_hf_backoff(
|
| 122 |
requests.post,
|
| 123 |
target_url,
|
| 124 |
headers=headers,
|
|
|
|
| 133 |
response.raise_for_status()
|
| 134 |
return response.json()
|
| 135 |
except requests.exceptions.RequestException as e:
|
| 136 |
+
return proxy_error_payload("Proxy Error (Hugging Face)", e)
|
| 137 |
except Exception as e:
|
| 138 |
return {"success": False, "message": f"Proxy Error (Internal): {str(e)}"}
|
| 139 |
|
|
|
|
| 156 |
data.setdefault("username", username)
|
| 157 |
|
| 158 |
def make_post():
|
| 159 |
+
return request_with_hf_backoff(
|
| 160 |
requests.post,
|
| 161 |
target_url,
|
| 162 |
headers=headers,
|
|
|
|
| 170 |
response.raise_for_status()
|
| 171 |
return response.json()
|
| 172 |
except requests.exceptions.RequestException as e:
|
| 173 |
+
return proxy_error_payload("Proxy Error (Hugging Face)", e, results=[])
|
| 174 |
except Exception as e:
|
| 175 |
return {"success": False, "message": f"Proxy Error (Internal): {str(e)}", "results": []}
|
| 176 |
|
|
|
|
| 183 |
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 184 |
|
| 185 |
def make_post():
|
| 186 |
+
return request_with_hf_backoff(
|
| 187 |
requests.post,
|
| 188 |
target_url,
|
| 189 |
headers=headers,
|
|
|
|
| 196 |
response.raise_for_status()
|
| 197 |
return response.json()
|
| 198 |
except requests.exceptions.RequestException as e:
|
| 199 |
+
return proxy_error_payload("Proxy Error (Hugging Face)", e, rows=[])
|
| 200 |
except Exception as e:
|
| 201 |
return {"success": False, "message": f"Proxy Error (Internal): {str(e)}", "rows": []}
|
| 202 |
|