feat(types): Resolve strict mypy errors in api_hook_client.py, models.py, and events.py

This commit is contained in:
2026-03-04 01:11:50 -05:00
parent b467107159
commit 6ebbf40d9d
4 changed files with 49 additions and 45 deletions

View File

@@ -1,5 +1,5 @@
from __future__ import annotations
import requests
import requests # type: ignore[import-untyped]
import json
import time
from typing import Any
@@ -23,7 +23,7 @@ class ApiHookClient:
time.sleep(0.1)
return False
def _make_request(self, method: str, endpoint: str, data: dict | None = None, timeout: float | None = None) -> dict | None:
def _make_request(self, method: str, endpoint: str, data: dict[str, Any] | None = None, timeout: float | None = None) -> dict[str, Any] | None:
url = f"{self.base_url}{endpoint}"
headers = {'Content-Type': 'application/json'}
last_exception = None
@@ -38,7 +38,8 @@ class ApiHookClient:
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
return response.json()
res_json = response.json()
return res_json if isinstance(res_json, dict) else None
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
last_exception = e
if attempt < self.max_retries:
@@ -55,49 +56,51 @@ class ApiHookClient:
raise ValueError(f"Failed to decode JSON from response for {endpoint}: {response.text}") from e
if last_exception:
raise last_exception
return None
def get_status(self) -> dict:
def get_status(self) -> dict[str, Any]:
"""Checks the health of the hook server."""
url = f"{self.base_url}/status"
try:
response = requests.get(url, timeout=5.0)
response.raise_for_status()
return response.json()
res = response.json()
return res if isinstance(res, dict) else {}
except Exception:
raise requests.exceptions.ConnectionError(f"Could not reach /status at {self.base_url}")
def get_project(self) -> dict | None:
def get_project(self) -> dict[str, Any] | None:
return self._make_request('GET', '/api/project')
def post_project(self, project_data: dict) -> dict | None:
def post_project(self, project_data: dict[str, Any]) -> dict[str, Any] | None:
return self._make_request('POST', '/api/project', data={'project': project_data})
def get_session(self) -> dict | None:
def get_session(self) -> dict[str, Any] | None:
res = self._make_request('GET', '/api/session')
return res
def get_mma_status(self) -> dict | None:
def get_mma_status(self) -> dict[str, Any] | None:
"""Retrieves current MMA status (track, tickets, tier, etc.)"""
return self._make_request('GET', '/api/gui/mma_status')
def push_event(self, event_type: str, payload: dict) -> dict | None:
def push_event(self, event_type: str, payload: dict[str, Any]) -> dict[str, Any] | None:
"""Pushes an event to the GUI's AsyncEventQueue via the /api/gui endpoint."""
return self.post_gui({
"action": event_type,
"payload": payload
})
def get_performance(self) -> dict | None:
def get_performance(self) -> dict[str, Any] | None:
"""Retrieves UI performance metrics."""
return self._make_request('GET', '/api/performance')
def post_session(self, session_entries: list) -> dict | None:
def post_session(self, session_entries: list[Any]) -> dict[str, Any] | None:
return self._make_request('POST', '/api/session', data={'session': {'entries': session_entries}})
def post_gui(self, gui_data: dict) -> dict | None:
def post_gui(self, gui_data: dict[str, Any]) -> dict[str, Any] | None:
return self._make_request('POST', '/api/gui', data=gui_data)
def select_tab(self, tab_bar: str, tab: str) -> dict | None:
def select_tab(self, tab_bar: str, tab: str) -> dict[str, Any] | None:
"""Tells the GUI to switch to a specific tab in a tab bar."""
return self.post_gui({
"action": "select_tab",
@@ -105,7 +108,7 @@ class ApiHookClient:
"tab": tab
})
def select_list_item(self, listbox: str, item_value: str) -> dict | None:
def select_list_item(self, listbox: str, item_value: str) -> dict[str, Any] | None:
"""Tells the GUI to select an item in a listbox by its value."""
return self.post_gui({
"action": "select_list_item",
@@ -113,7 +116,7 @@ class ApiHookClient:
"item_value": item_value
})
def set_value(self, item: str, value: Any) -> dict | None:
def set_value(self, item: str, value: Any) -> dict[str, Any] | None:
"""Sets the value of a GUI item."""
return self.post_gui({
"action": "set_value",
@@ -144,7 +147,7 @@ class ApiHookClient:
try:
# Fallback for thinking/live/prior which are in diagnostics
diag = self._make_request('GET', '/api/gui/diagnostics')
if item in diag:
if diag and item in diag:
return diag[item]
# Map common indicator tags to diagnostics keys
mapping = {
@@ -153,7 +156,7 @@ class ApiHookClient:
"prior_session_indicator": "prior"
}
key = mapping.get(item)
if key and key in diag:
if diag and key and key in diag:
return diag[key]
except Exception:
pass
@@ -171,15 +174,15 @@ class ApiHookClient:
return val
try:
diag = self._make_request('GET', '/api/gui/diagnostics')
if 'nodes' in diag and node_tag in diag['nodes']:
if diag and 'nodes' in diag and node_tag in diag['nodes']:
return diag['nodes'][node_tag]
if node_tag in diag:
if diag and node_tag in diag:
return diag[node_tag]
except Exception:
pass
return None
def click(self, item: str, *args: Any, **kwargs: Any) -> dict | None:
def click(self, item: str, *args: Any, **kwargs: Any) -> dict[str, Any] | None:
"""Simulates a click on a GUI button or item."""
user_data = kwargs.pop('user_data', None)
return self.post_gui({
@@ -190,7 +193,7 @@ class ApiHookClient:
"user_data": user_data
})
def get_indicator_state(self, tag: str) -> dict:
def get_indicator_state(self, tag: str) -> dict[str, Any]:
"""Checks if an indicator is shown using the diagnostics endpoint."""
# Mapping tag to the keys used in diagnostics endpoint
mapping = {
@@ -201,24 +204,25 @@ class ApiHookClient:
key = mapping.get(tag, tag)
try:
diag = self._make_request('GET', '/api/gui/diagnostics')
return {"tag": tag, "shown": diag.get(key, False)}
return {"tag": tag, "shown": diag.get(key, False) if diag else False}
except Exception as e:
return {"tag": tag, "shown": False, "error": str(e)}
def get_events(self) -> list:
def get_events(self) -> list[Any]:
"""Fetches and clears the event queue from the server."""
try:
return self._make_request('GET', '/api/events').get("events", [])
res = self._make_request('GET', '/api/events')
return res.get("events", []) if res else []
except Exception:
return []
def wait_for_event(self, event_type: str, timeout: float = 5) -> dict | None:
def wait_for_event(self, event_type: str, timeout: float = 5) -> dict[str, Any] | None:
"""Polls for a specific event type."""
start = time.time()
while time.time() - start < timeout:
events = self.get_events()
for ev in events:
if ev.get("type") == event_type:
if isinstance(ev, dict) and ev.get("type") == event_type:
return ev
time.sleep(0.1) # Fast poll
return None
@@ -232,14 +236,14 @@ class ApiHookClient:
time.sleep(0.1) # Fast poll
return False
def reset_session(self) -> dict | None:
def reset_session(self) -> dict[str, Any] | None:
"""Simulates clicking the 'Reset Session' button in the GUI."""
return self.click("btn_reset")
def request_confirmation(self, tool_name: str, args: dict) -> Any:
def request_confirmation(self, tool_name: str, args: dict[str, Any]) -> Any:
"""Asks the user for confirmation via the GUI (blocking call)."""
# Using a long timeout as this waits for human input (60 seconds)
res = self._make_request('POST', '/api/ask',
data={'type': 'tool_approval', 'tool': tool_name, 'args': args},
timeout=60.0)
return res.get('response')
return res.get('response') if res else None