fix: skip unsupported image profile refreshes
This commit is contained in:
@@ -24,7 +24,7 @@ from typing import Any
|
|||||||
|
|
||||||
|
|
||||||
APP_NAME = "codex-retry-gateway-tui"
|
APP_NAME = "codex-retry-gateway-tui"
|
||||||
FALLBACK_VERSION = "0.1.9"
|
FALLBACK_VERSION = "0.1.10"
|
||||||
DEFAULT_GATEWAY_ADMIN_PATH = "/__codex_retry_gateway"
|
DEFAULT_GATEWAY_ADMIN_PATH = "/__codex_retry_gateway"
|
||||||
DEFAULT_GATEWAY_URL = "http://127.0.0.1:4610/__codex_retry_gateway"
|
DEFAULT_GATEWAY_URL = "http://127.0.0.1:4610/__codex_retry_gateway"
|
||||||
DEFAULT_API_URL = DEFAULT_GATEWAY_URL
|
DEFAULT_API_URL = DEFAULT_GATEWAY_URL
|
||||||
@@ -2023,6 +2023,17 @@ def open_url(url: str) -> None:
|
|||||||
subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
def gateway_supports_image_profiles(status_payload: dict[str, Any]) -> bool:
|
||||||
|
config = status_payload.get("config") if isinstance(status_payload.get("config"), dict) else {}
|
||||||
|
state = status_payload.get("state") if isinstance(status_payload.get("state"), dict) else {}
|
||||||
|
paths = status_payload.get("paths") if isinstance(status_payload.get("paths"), dict) else {}
|
||||||
|
return (
|
||||||
|
"image_profile_name" in config
|
||||||
|
or "image_profile_name" in state
|
||||||
|
or "image_profiles_dir" in paths
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def fetch_dashboard_snapshot(
|
def fetch_dashboard_snapshot(
|
||||||
gateway_url: str,
|
gateway_url: str,
|
||||||
status_url: str,
|
status_url: str,
|
||||||
@@ -2033,29 +2044,35 @@ def fetch_dashboard_snapshot(
|
|||||||
current_view: str = "overview",
|
current_view: str = "overview",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
gateway_root = gateway_admin_url(gateway_url)
|
gateway_root = gateway_admin_url(gateway_url)
|
||||||
|
status_endpoint = status_url or gateway_status_url(gateway_root)
|
||||||
|
|
||||||
|
status_payload, status_error = fetch_optional_payload(status_endpoint, timeout, access_key)
|
||||||
|
payload = status_payload if isinstance(status_payload, dict) else {}
|
||||||
endpoints = {
|
endpoints = {
|
||||||
"status": status_url or gateway_status_url(gateway_root),
|
|
||||||
"logs": build_api_url(gateway_root, "/api/logs", {"limit": 200}),
|
"logs": build_api_url(gateway_root, "/api/logs", {"limit": 200}),
|
||||||
"requests": build_api_url(gateway_root, "/api/requests", {"limit": 200}),
|
"requests": build_api_url(gateway_root, "/api/requests", {"limit": 200}),
|
||||||
"profiles": build_api_url(gateway_root, "/api/profiles"),
|
"profiles": build_api_url(gateway_root, "/api/profiles"),
|
||||||
"image_profiles": build_api_url(gateway_root, "/api/image-profiles"),
|
|
||||||
}
|
}
|
||||||
|
image_profiles_supported = gateway_supports_image_profiles(payload)
|
||||||
|
if image_profiles_supported:
|
||||||
|
endpoints["image_profiles"] = build_api_url(gateway_root, "/api/image-profiles")
|
||||||
|
|
||||||
def fetch_optional(url: str, *, allow_not_found: bool = False) -> tuple[dict[str, Any], str]:
|
def fetch_optional(url: str, *, allow_not_found: bool = False) -> tuple[dict[str, Any], str]:
|
||||||
return fetch_optional_payload(url, timeout, access_key, allow_not_found=allow_not_found)
|
return fetch_optional_payload(url, timeout, access_key, allow_not_found=allow_not_found)
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=5) as pool:
|
with ThreadPoolExecutor(max_workers=len(endpoints)) as pool:
|
||||||
futures = {
|
futures = {
|
||||||
name: pool.submit(fetch_optional, url, allow_not_found=name == "image_profiles")
|
name: pool.submit(fetch_optional, url, allow_not_found=name == "image_profiles")
|
||||||
for name, url in endpoints.items()
|
for name, url in endpoints.items()
|
||||||
}
|
}
|
||||||
status_payload, status_error = futures["status"].result()
|
|
||||||
logs_payload, logs_error = futures["logs"].result()
|
logs_payload, logs_error = futures["logs"].result()
|
||||||
requests_payload, requests_error = futures["requests"].result()
|
requests_payload, requests_error = futures["requests"].result()
|
||||||
profiles_payload, profiles_error = futures["profiles"].result()
|
profiles_payload, profiles_error = futures["profiles"].result()
|
||||||
|
if image_profiles_supported:
|
||||||
image_profiles_payload, image_profiles_error = futures["image_profiles"].result()
|
image_profiles_payload, image_profiles_error = futures["image_profiles"].result()
|
||||||
|
else:
|
||||||
|
image_profiles_payload, image_profiles_error = {}, ""
|
||||||
|
|
||||||
payload = status_payload if isinstance(status_payload, dict) else {}
|
|
||||||
requests_source = requests_payload if isinstance(requests_payload, dict) else {}
|
requests_source = requests_payload if isinstance(requests_payload, dict) else {}
|
||||||
logs_source = logs_payload if isinstance(logs_payload, dict) else {}
|
logs_source = logs_payload if isinstance(logs_payload, dict) else {}
|
||||||
profiles_source = profiles_payload if isinstance(profiles_payload, dict) else {}
|
profiles_source = profiles_payload if isinstance(profiles_payload, dict) else {}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "codex-retry-gateway-tui"
|
name = "codex-retry-gateway-tui"
|
||||||
version = "0.1.9"
|
version = "0.1.10"
|
||||||
description = "Terminal UI for codex-retry-gateway monitoring and control"
|
description = "Terminal UI for codex-retry-gateway monitoring and control"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|||||||
@@ -139,6 +139,35 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
|||||||
self.assertEqual(payload, {})
|
self.assertEqual(payload, {})
|
||||||
self.assertEqual(error, "")
|
self.assertEqual(error, "")
|
||||||
|
|
||||||
|
def test_dashboard_skips_image_profiles_endpoint_when_status_lacks_capability(self) -> None:
|
||||||
|
mod = load_module()
|
||||||
|
calls: list[str] = []
|
||||||
|
|
||||||
|
def fetch(url: str, timeout: int, access_key: str = "", *, allow_not_found: bool = False):
|
||||||
|
calls.append(url)
|
||||||
|
if url.endswith("/api/status"):
|
||||||
|
return {"config": {"profile_name": "legacy"}, "state": {}, "paths": {}}, ""
|
||||||
|
if "/api/requests" in url:
|
||||||
|
return {"entries": []}, ""
|
||||||
|
if "/api/logs" in url:
|
||||||
|
return {"entries": []}, ""
|
||||||
|
if "/api/profiles" in url:
|
||||||
|
return {"profiles": []}, ""
|
||||||
|
self.fail(f"unexpected endpoint {url}")
|
||||||
|
|
||||||
|
with mock.patch.object(mod, "fetch_optional_payload", side_effect=fetch):
|
||||||
|
snapshot = mod.fetch_dashboard_snapshot("http://127.0.0.1:4610", "", 5)
|
||||||
|
|
||||||
|
self.assertEqual(snapshot["image_profiles"], [])
|
||||||
|
self.assertEqual(snapshot["status_error"], "")
|
||||||
|
self.assertFalse(any("/api/image-profiles" in url for url in calls))
|
||||||
|
|
||||||
|
def test_gateway_image_profiles_capability_uses_status_field_presence(self) -> None:
|
||||||
|
mod = load_module()
|
||||||
|
self.assertFalse(mod.gateway_supports_image_profiles({"config": {"profile_name": "legacy"}}))
|
||||||
|
self.assertTrue(mod.gateway_supports_image_profiles({"config": {"image_profile_name": ""}}))
|
||||||
|
self.assertTrue(mod.gateway_supports_image_profiles({"paths": {"image_profiles_dir": "/tmp/profiles"}}))
|
||||||
|
|
||||||
def test_request_usage_summary_shows_cached_ratio(self) -> None:
|
def test_request_usage_summary_shows_cached_ratio(self) -> None:
|
||||||
mod = load_module()
|
mod = load_module()
|
||||||
row = {
|
row = {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ requires-python = ">=3.11"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codex-retry-gateway-tui"
|
name = "codex-retry-gateway-tui"
|
||||||
version = "0.1.9"
|
version = "0.1.10"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "textual" },
|
{ name = "textual" },
|
||||||
|
|||||||
Reference in New Issue
Block a user