feat: show req and resp ids in tui

This commit is contained in:
2026-07-01 10:03:20 +08:00
parent 58f3caeea5
commit 5c7bea431a
4 changed files with 41 additions and 25 deletions
+23 -13
View File
@@ -22,7 +22,7 @@ from typing import Any
APP_NAME = "codex-retry-gateway-tui"
FALLBACK_VERSION = "0.1.0"
FALLBACK_VERSION = "0.1.4"
DEFAULT_GATEWAY_ADMIN_PATH = "/__codex_retry_gateway"
DEFAULT_GATEWAY_URL = "http://127.0.0.1:4610/__codex_retry_gateway"
DEFAULT_API_URL = DEFAULT_GATEWAY_URL
@@ -386,8 +386,12 @@ def effective_input_tokens(input_tokens: Any, cached_tokens: Any) -> int | None:
return max(0, total - cached)
def primary_request_id(row: dict[str, Any]) -> str:
return str(row.get("response_id") or row.get("request_id") or "").strip() or "-"
def request_id_text(row: dict[str, Any]) -> str:
return str(row.get("request_id") or "").strip() or "-"
def response_id_text(row: dict[str, Any]) -> str:
return str(row.get("response_id") or "").strip() or "-"
def request_usage_summary(row: dict[str, Any]) -> str:
@@ -474,7 +478,8 @@ def request_row_cells(row: dict[str, Any], text_limits: dict[str, int] | None =
note_width = max(4, limits.get("note", 22) - len(note_prefix))
return (
str(row["seq"]),
short_text(primary_request_id(row), limits.get("id", 18)) or "-",
short_text(request_id_text(row), limits.get("req_id", 18)) or "-",
short_text(response_id_text(row), limits.get("resp_id", 18)) or "-",
short_text(row["thread_id"], limits.get("thread", 18)) or "-",
short_time(row["started_at"]),
f"{status_symbol(row)} {row.get('status_code') or '-'}",
@@ -776,7 +781,8 @@ def normalize_request_rows(payload: dict[str, Any], filter_text: str = "") -> li
def render_request_detail(row: dict[str, Any]) -> str:
bits = [
f"seq {row['seq']}",
f"id {primary_request_id(row)}",
f"req {request_id_text(row)}",
f"resp {response_id_text(row)}",
f"thread {row.get('thread_id') or '-'}",
f"started {short_time(row['started_at'])}",
f"{row['method']} {row['path']}",
@@ -1214,7 +1220,8 @@ def run_textual(
self.request_table_width_profiles = {
"compact": {
"seq": 7,
"id": 18,
"req_id": 18,
"resp_id": 18,
"thread": 18,
"started": 19,
"status": 7,
@@ -1232,7 +1239,8 @@ def run_textual(
},
"wide": {
"seq": 7,
"id": 36,
"req_id": 24,
"resp_id": 24,
"thread": 36,
"started": 19,
"status": 7,
@@ -1272,7 +1280,8 @@ def run_textual(
width_profile = self.request_table_width_profiles[self.request_table_density]
self.request_table_column_keys = {
"seq": requests.add_column("Seq", width=width_profile["seq"], key="seq"),
"id": requests.add_column("ID", width=width_profile["id"], key="id"),
"req_id": requests.add_column("Req ID", width=width_profile["req_id"], key="req_id"),
"resp_id": requests.add_column("Resp ID", width=width_profile["resp_id"], key="resp_id"),
"thread": requests.add_column("Thread", width=width_profile["thread"], key="thread"),
"started": requests.add_column("Started", width=width_profile["started"], key="started"),
"status": requests.add_column("Status", width=width_profile["status"], key="status"),
@@ -1280,8 +1289,8 @@ def run_textual(
"model": requests.add_column("Model", width=width_profile["model"], key="model"),
"reasoning": requests.add_column("Reasoning", width=width_profile["reasoning"], key="reasoning"),
"usage": requests.add_column("Usage", width=self.request_usage_width, key="usage"),
"req": requests.add_column("Req", width=width_profile["req"], key="req"),
"resp": requests.add_column("Resp", width=width_profile["resp"], key="resp"),
"req_bytes": requests.add_column("Req Size", width=width_profile["req"], key="req_bytes"),
"resp_bytes": requests.add_column("Resp Size", width=width_profile["resp"], key="resp_bytes"),
"chunks": requests.add_column("Chunks", width=width_profile["chunks"], key="chunks"),
"first": requests.add_column("First", width=width_profile["first"], key="first"),
"duration": requests.add_column("Duration", width=width_profile["duration"], key="duration"),
@@ -1559,14 +1568,15 @@ def run_textual(
def _request_text_limits(self, table: DataTable) -> dict[str, int]:
width_profile = self.request_table_width_profiles[self.request_table_density]
limits = {
"id": width_profile["id"],
"req_id": width_profile["req_id"],
"resp_id": width_profile["resp_id"],
"thread": width_profile["thread"],
"path": width_profile["path"],
"model": width_profile["model"],
"usage": self.request_usage_width,
"note": width_profile["note"],
}
for name in ("id", "thread", "path", "model", "usage", "note"):
for name in ("req_id", "resp_id", "thread", "path", "model", "usage", "note"):
key = self.request_table_column_keys.get(name)
column = table.columns.get(key) if key is not None else None
if column is None or not getattr(column, "width", 0):
@@ -1791,7 +1801,7 @@ def print_once(snapshot: dict[str, Any], filter_text: str = "") -> None:
for row in snapshot.get("requests") or []:
try:
print(
f"{row['seq']:>6} {short_text(primary_request_id(row), 16):<16} {short_text(row.get('thread_id'), 14):<14} {short_time(row.get('started_at')):<19} {status_symbol(row)} {row.get('status_code') or '-':<4} "
f"{row['seq']:>6} {short_text(request_id_text(row), 16):<16} {short_text(response_id_text(row), 16):<16} {short_text(row.get('thread_id'), 14):<14} {short_time(row.get('started_at')):<19} {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"{short_text(request_usage_summary(row), 26):<26} {format_bytes(row.get('request_body_bytes')):<8} {request_chunk_progress(row):<14} {format_duration_ms_as_seconds(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
View File
@@ -1,6 +1,6 @@
[project]
name = "codex-retry-gateway-tui"
version = "0.1.3"
version = "0.1.4"
description = "Terminal UI for codex-retry-gateway monitoring and control"
readme = "README.md"
requires-python = ">=3.11"
+16 -10
View File
@@ -94,8 +94,11 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
"finished_at": "2026-06-30T00:00:01Z",
"usage_last_updated_at": "2026-06-30T00:00:01Z",
}
self.assertEqual(mod.primary_request_id(row), "resp_1")
self.assertEqual(mod.request_id_text(row), "r")
self.assertEqual(mod.response_id_text(row), "resp_1")
self.assertIn("thread thread_1", mod.render_request_detail(row))
self.assertIn("req r", mod.render_request_detail(row))
self.assertIn("resp resp_1", mod.render_request_detail(row))
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")
@@ -136,15 +139,16 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
"error": "Selected model is at capacity. Please try a different model.",
"finished_at": "2026-06-30T12:25:00Z",
}
compact = mod.request_row_cells(row, {"id": 18, "thread": 18, "path": 22, "model": 16, "usage": 24, "note": 22})
wide = mod.request_row_cells(row, {"id": 36, "thread": 36, "path": 40, "model": 24, "usage": 72, "note": 48})
self.assertEqual(compact[9], "0B")
self.assertEqual(wide[9], "0B")
compact = mod.request_row_cells(row, {"req_id": 18, "resp_id": 18, "thread": 18, "path": 22, "model": 16, "usage": 24, "note": 22})
wide = mod.request_row_cells(row, {"req_id": 36, "resp_id": 36, "thread": 36, "path": 40, "model": 24, "usage": 72, "note": 48})
self.assertEqual(compact[10], "0B")
self.assertEqual(wide[10], "0B")
self.assertLess(len(compact[1]), len(wide[1]))
self.assertLess(len(compact[2]), len(wide[2]))
self.assertLess(len(compact[5]), len(wide[5]))
self.assertLess(len(compact[8]), len(wide[8]))
self.assertLess(len(compact[15]), len(wide[15]))
self.assertLess(len(compact[3]), len(wide[3]))
self.assertLess(len(compact[6]), len(wide[6]))
self.assertLess(len(compact[9]), len(wide[9]))
self.assertLess(len(compact[16]), len(wide[16]))
def test_request_row_cells_show_request_bytes(self) -> None:
mod = load_module()
@@ -174,8 +178,10 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
"finished_at": "2026-06-30T12:00:03Z",
}
cells = mod.request_row_cells(row)
self.assertEqual(cells[9], "1.5KB")
self.assertEqual(cells[10], "4.1KB")
self.assertEqual(cells[1], "req_1")
self.assertEqual(cells[2], "resp_1")
self.assertEqual(cells[10], "1.5KB")
self.assertEqual(cells[11], "4.1KB")
def test_request_rows_read_usage_from_nested_usage_object(self) -> None:
mod = load_module()
Generated
+1 -1
View File
@@ -4,7 +4,7 @@ requires-python = ">=3.11"
[[package]]
name = "codex-retry-gateway-tui"
version = "0.1.0"
version = "0.1.4"
source = { editable = "." }
dependencies = [
{ name = "textual" },