adrija93's picture
Fixing bugs 2.0
65602c7 verified
Raw History Blame Contribute Delete
2.72 kB
"""Service to handle task result notifications with retries and diagnostics."""
from typing import Dict, Any
import httpx
from pydantic import HttpUrl
from tenacity import retry, stop_after_attempt, wait_exponential
class NotificationService:
"""Service for sending notifications with retry logic."""
def __init__(self, max_retries: int = 5, timeout: float = 30.0):
"""Create a reusable HTTP client.
timeout can be a float or seconds; we prefer a more generous read timeout on Spaces.
"""
# Increase timeouts to accommodate slower evaluators
timeout_cfg = httpx.Timeout(
connect=10.0,
read=max(60.0, float(timeout)),
write=30.0,
pool=10.0,
)
# Follow redirects to ensure we validate the final response code
self.client = httpx.AsyncClient(timeout=timeout_cfg, follow_redirects=True, headers={
"User-Agent": "TDS-Project1-API/1.0"
})
self.max_retries = max_retries
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=1, max=60),
reraise=True
)
async def notify_evaluation(self, url: str | HttpUrl, task_result: Dict[str, Any]) -> None:
"""
Send task result to evaluation URL with retries.
Args:
url: The evaluation URL to send results to (str or HttpUrl)
task_result: The task result data to send
Raises:
httpx.HTTPError: If the request fails after all retries
"""
# Convert HttpUrl to string if needed
url_str = str(url) if not isinstance(url, str) else url
try:
response = await self.client.post(
url_str,
json=task_result,
headers={"Content-Type": "application/json"}
)
except httpx.RequestError as e:
# Provide clearer diagnostics for network-level errors
print(f"[Notify] Request error to {url_str}: {e}")
raise
# Enforce strict 200 OK per requirement (not just any 2xx)
if response.status_code != 200:
body_preview = (response.text or "").strip()
if len(body_preview) > 512:
body_preview = body_preview[:512] + "..."
print(f"[Notify] Non-200 from evaluator: {response.status_code}. Body: {body_preview}")
raise httpx.HTTPStatusError(
f"Expected 200 OK, got {response.status_code}", request=response.request, response=response
)
async def close(self):
"""Close the HTTP client."""
await self.client.aclose()