import requests import json import time class ApiHookClient: def __init__(self, base_url="http://127.0.0.1:8999", max_retries=3, retry_delay=1): self.base_url = base_url self.max_retries = max_retries self.retry_delay = retry_delay def wait_for_server(self, timeout=10): """ Polls the /status endpoint until the server is ready or timeout is reached. """ start_time = time.time() while time.time() - start_time < timeout: try: if self.get_status().get('status') == 'ok': return True except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): time.sleep(0.5) return False def _make_request(self, method, endpoint, data=None): url = f"{self.base_url}{endpoint}" headers = {'Content-Type': 'application/json'} last_exception = None for attempt in range(self.max_retries + 1): try: if method == 'GET': response = requests.get(url, timeout=2) elif method == 'POST': response = requests.post(url, json=data, headers=headers, timeout=2) else: raise ValueError(f"Unsupported HTTP method: {method}") response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx) return response.json() except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e: last_exception = e if attempt < self.max_retries: time.sleep(self.retry_delay) continue else: if isinstance(e, requests.exceptions.Timeout): raise requests.exceptions.Timeout(f"Request to {endpoint} timed out after {self.max_retries} retries.") from e else: raise requests.exceptions.ConnectionError(f"Could not connect to API hook server at {self.base_url} after {self.max_retries} retries.") from e except requests.exceptions.HTTPError as e: raise requests.exceptions.HTTPError(f"HTTP error {e.response.status_code} for {endpoint}: {e.response.text}") from e except json.JSONDecodeError as e: raise ValueError(f"Failed to decode JSON from response for {endpoint}: {response.text}") from e if last_exception: raise last_exception def get_status(self): """Checks the health of the hook server.""" url = f"{self.base_url}/status" try: response = requests.get(url, timeout=1) response.raise_for_status() return response.json() except Exception: raise requests.exceptions.ConnectionError(f"Could not reach /status at {self.base_url}") def get_project(self): return self._make_request('GET', '/api/project') def post_project(self, project_data): return self._make_request('POST', '/api/project', data={'project': project_data}) def get_session(self): return self._make_request('GET', '/api/session') def get_performance(self): """Retrieves UI performance metrics.""" return self._make_request('GET', '/api/performance') def post_session(self, session_entries): return self._make_request('POST', '/api/session', data={'session': {'entries': session_entries}}) def post_gui(self, gui_data): return self._make_request('POST', '/api/gui', data=gui_data)