fix: tighten tui layout and request progress
This commit is contained in:
@@ -359,6 +359,24 @@ def age_since(value: Any) -> str:
|
||||
return format_elapsed_seconds(seconds)
|
||||
|
||||
|
||||
def request_updated_at(row: dict[str, Any]) -> Any:
|
||||
for key in ("last_activity_at", "usage_last_updated_at", "finished_at"):
|
||||
value = row.get(key)
|
||||
if parse_datetime(value):
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def request_updated_elapsed(row: dict[str, Any]) -> str:
|
||||
return elapsed_between(row.get("started_at"), request_updated_at(row))
|
||||
|
||||
|
||||
def request_chunk_progress(row: dict[str, Any]) -> str:
|
||||
if not row.get("response_stream"):
|
||||
return "-"
|
||||
return f"{row.get('stream_chunk_count') or 0} / {format_bytes(row.get('response_bytes_received'))}"
|
||||
|
||||
|
||||
def status_kind(value: Any) -> str:
|
||||
text = str(value or "").strip().lower()
|
||||
if text in {"ok", "operational", "success"}:
|
||||
@@ -558,7 +576,7 @@ def render_request_detail(row: dict[str, Any]) -> str:
|
||||
f"life {request_lifecycle_label(row)}",
|
||||
f"started {short_time(row['started_at'])}",
|
||||
f"finished {short_time(row['finished_at'])}",
|
||||
f"updated {age_since(row['usage_last_updated_at'])}",
|
||||
f"updated {request_updated_elapsed(row)}",
|
||||
]
|
||||
if row.get("model"):
|
||||
bits.append(f"model {row['model']}")
|
||||
@@ -753,11 +771,11 @@ def run_textual(api_url: str, status_url: str, refresh_seconds: int, timeout: in
|
||||
Screen { layout: vertical; }
|
||||
#summary { height: 1; padding: 0 1; color: $accent; }
|
||||
#paths { height: 1; padding: 0 1; color: $text-muted; }
|
||||
#controls { height: 2; }
|
||||
#tables { height: 1fr; }
|
||||
#controls { height: 1; padding: 0 1; color: $text-muted; }
|
||||
#tables { height: 1; padding: 0 1; color: $text-muted; }
|
||||
#requests_table, #logs_table, #profiles_table { height: 1fr; }
|
||||
#detail { height: 5; padding: 0 1; border-top: solid $panel; }
|
||||
#status { height: 2; padding: 0 1; color: $text-muted; }
|
||||
#detail { height: 3; padding: 0 1; border-top: solid $panel; }
|
||||
#status { height: 1; padding: 0 1; color: $text-muted; }
|
||||
"""
|
||||
|
||||
BINDINGS = [
|
||||
@@ -813,7 +831,7 @@ def run_textual(api_url: str, status_url: str, refresh_seconds: int, timeout: in
|
||||
requests = self.query_one("#requests_table", DataTable)
|
||||
requests.cursor_type = "row"
|
||||
requests.zebra_stripes = True
|
||||
requests.add_columns("Seq", "ID", "Status", "Path", "Model", "Reasoning", "Req", "Resp", "First", "Duration", "Updated", "Note")
|
||||
requests.add_columns("Seq", "ID", "Status", "Path", "Model", "Reasoning", "Req", "Resp", "Chunks", "First", "Duration", "Updated", "Note")
|
||||
|
||||
logs = self.query_one("#logs_table", DataTable)
|
||||
logs.cursor_type = "row"
|
||||
@@ -994,9 +1012,10 @@ def run_textual(api_url: str, status_url: str, refresh_seconds: int, timeout: in
|
||||
row["reasoning_tokens"] if row["reasoning_tokens"] is not None else "-",
|
||||
format_bytes(row["request_body_bytes"]),
|
||||
format_bytes(row["response_bytes_received"]),
|
||||
request_chunk_progress(row),
|
||||
format_ms(row["first_response_delay_ms"]),
|
||||
format_ms(row["duration_ms"]),
|
||||
age_since(row.get("usage_last_updated_at")),
|
||||
request_updated_elapsed(row),
|
||||
f"{row['upstream_attempt_count'] or 0}x {short_text(row['error'], 18)}",
|
||||
key=key,
|
||||
)
|
||||
@@ -1236,7 +1255,7 @@ def print_once(snapshot: dict[str, Any], filter_text: str = "") -> None:
|
||||
print(
|
||||
f"{row['seq']:>6} {short_text(row['request_id'], 16):<16} {status_symbol(row)} {row.get('status_code') or '-':<4} "
|
||||
f"{short_text(row['path'], 20):<20} {short_text(row['model'] or row['requested_model'] or row['forwarded_model'], 16):<16} "
|
||||
f"{format_ms(row['duration_ms']):<8} {age_since(row.get('usage_last_updated_at')):<8} "
|
||||
f"{request_chunk_progress(row):<14} {format_ms(row['duration_ms']):<8} {request_updated_elapsed(row):<8} "
|
||||
f"{format_count(row['upstream_attempt_count'] or 0):<4} {short_text(row['error'], 30)}"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "codex-retry-gateway-tui"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
description = "Terminal UI for codex-retry-gateway monitoring and control"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
+21
-1
@@ -72,7 +72,27 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
mod.gateway_status_url("http://127.0.0.1:4610/__codex_retry_gateway"),
|
||||
"http://127.0.0.1:4610/__codex_retry_gateway/api/status",
|
||||
)
|
||||
self.assertIn("updated", mod.render_request_detail({"seq": 1, "request_id": "r", "method": "POST", "path": "/responses", "status_code": 200, "upstream_status_code": 200, "upstream_attempt_count": 1, "first_response_delay_ms": 10, "duration_ms": 20, "request_body_bytes": 3, "response_bytes_received": 4, "stream_chunk_count": 1, "started_at": "2026-06-30T00:00:00Z", "finished_at": "2026-06-30T00:00:01Z", "usage_last_updated_at": "2026-06-30T00:00:01Z"}))
|
||||
row = {
|
||||
"seq": 1,
|
||||
"request_id": "r",
|
||||
"method": "POST",
|
||||
"path": "/responses",
|
||||
"status_code": 200,
|
||||
"upstream_status_code": 200,
|
||||
"upstream_attempt_count": 1,
|
||||
"first_response_delay_ms": 10,
|
||||
"duration_ms": 20,
|
||||
"request_body_bytes": 3,
|
||||
"response_bytes_received": 4096,
|
||||
"stream_chunk_count": 7,
|
||||
"response_stream": True,
|
||||
"started_at": "2026-06-30T00:00:00Z",
|
||||
"finished_at": "2026-06-30T00:00:01Z",
|
||||
"usage_last_updated_at": "2026-06-30T00:00:01Z",
|
||||
}
|
||||
self.assertIn("updated 1.0s", mod.render_request_detail(row))
|
||||
self.assertEqual(mod.request_updated_elapsed(row), "1.0s")
|
||||
self.assertEqual(mod.request_chunk_progress(row), "7 / 4.1KB")
|
||||
|
||||
def test_default_api_url_discovers_gateway_state(self) -> None:
|
||||
mod = load_module()
|
||||
|
||||
Reference in New Issue
Block a user