"""Client for sending notifications to evaluation URL.""" import asyncio from typing import Any import httpx from tenacity import retry, stop_after_attempt, wait_exponential from shared.config import settings from shared.logger import setup_logger from shared.models import RepoSubmission logger = setup_logger(__name__) class NotificationClient: """Client for notifying evaluation endpoint about repo submissions.""" def __init__(self) -> None: """Initialize notification client.""" self.retry_delays = settings.get_retry_delays() self.max_retries = settings.max_retry_attempts @retry( stop=stop_after_attempt(4), wait=wait_exponential(multiplier=1, min=1, max=8), reraise=True, ) async def notify(self, evaluation_url: str, submission: RepoSubmission) -> bool: """Send repo submission to evaluation URL with retry logic. Args: evaluation_url: URL to send notification to submission: Repository submission data Returns: True if successful, False otherwise """ logger.info(f"Sending notification to {evaluation_url}") logger.debug(f"Submission data: {submission.model_dump()}") try: async with httpx.AsyncClient(timeout=30.0) as client: response = await client.post( evaluation_url, json=submission.model_dump(), headers={"Content-Type": "application/json"}, ) if response.status_code == 200: logger.info(f"Successfully notified {evaluation_url}") return True else: logger.error( f"Failed to notify {evaluation_url}: " f"Status {response.status_code}, Response: {response.text}" ) raise Exception(f"HTTP {response.status_code}: {response.text}") except httpx.TimeoutException: logger.error(f"Timeout while notifying {evaluation_url}") raise except Exception as e: logger.error(f"Error notifying {evaluation_url}: {e}") raise async def notify_with_timeout( self, evaluation_url: str, submission: RepoSubmission, timeout_minutes: int = 10 ) -> bool: """Send notification with overall timeout. Args: evaluation_url: URL to send notification to submission: Repository submission data timeout_minutes: Overall timeout in minutes Returns: True if successful, False otherwise """ try: return await asyncio.wait_for( self.notify(evaluation_url, submission), timeout=timeout_minutes * 60, ) except asyncio.TimeoutError: logger.error(f"Overall timeout of {timeout_minutes} minutes exceeded") return False