route optimizer agent with dailygrubs ai assign
This commit is contained in:
@@ -43,8 +43,22 @@ async def api_patch(url: str, **kwargs) -> Optional[Dict[str, Any]]:
|
||||
return await _request("patch", url, **kwargs)
|
||||
|
||||
|
||||
async def _safe_text(resp) -> str:
|
||||
try:
|
||||
return (await resp.text())[:300]
|
||||
except Exception:
|
||||
return "<unreadable body>"
|
||||
|
||||
|
||||
async def _request(method: str, url: str, **kwargs) -> Optional[Dict[str, Any]]:
|
||||
"""Execute an HTTP request with up to 3 attempts and exponential backoff."""
|
||||
"""Execute an HTTP request with up to 3 attempts and exponential backoff.
|
||||
|
||||
Returns the parsed body ONLY on a 2xx/3xx response. A 4xx is a client error
|
||||
and returns None immediately (no retry — it won't fix itself). A 5xx is
|
||||
retried like a network error, then returns None. This is the difference
|
||||
between "the action succeeded" and "the server rejected it": callers gate on
|
||||
a non-None result, so an error response must not look like success.
|
||||
"""
|
||||
max_retries = 3
|
||||
backoff = 1.0
|
||||
session = await get_session()
|
||||
@@ -52,6 +66,25 @@ async def _request(method: str, url: str, **kwargs) -> Optional[Dict[str, Any]]:
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
async with getattr(session, method)(url, **kwargs) as resp:
|
||||
if resp.status >= 500:
|
||||
body = await _safe_text(resp)
|
||||
if attempt < max_retries - 1:
|
||||
logger.warning(
|
||||
f"{method.upper()} {url} -> {resp.status} (server error), "
|
||||
f"retrying in {backoff:.0f}s: {body}"
|
||||
)
|
||||
await asyncio.sleep(backoff)
|
||||
backoff *= 2
|
||||
continue
|
||||
logger.error(
|
||||
f"{method.upper()} {url} -> {resp.status} after {max_retries} attempts: {body}"
|
||||
)
|
||||
return None
|
||||
if resp.status >= 400:
|
||||
logger.error(
|
||||
f"{method.upper()} {url} -> {resp.status} (client error): {await _safe_text(resp)}"
|
||||
)
|
||||
return None
|
||||
if resp.content_type == "application/json":
|
||||
return await resp.json()
|
||||
return {"status_code": resp.status}
|
||||
|
||||
Reference in New Issue
Block a user