From 58f3caeea587be0671ef3d8bf41889a34d84e276 Mon Sep 17 00:00:00 2001 From: yunyaozhou Date: Wed, 1 Jul 2026 09:50:06 +0800 Subject: [PATCH] feat: show request payload size --- codex_retry_gateway_tui.py | 20 ++++++++++++++------ tests/test_payload.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/codex_retry_gateway_tui.py b/codex_retry_gateway_tui.py index 7bd23e5..a8dfd10 100644 --- a/codex_retry_gateway_tui.py +++ b/codex_retry_gateway_tui.py @@ -346,9 +346,13 @@ def format_duration_ms_as_seconds(value: Any) -> str: def format_bytes(value: Any) -> str: - number = as_int(value) - if number <= 0: + if value is None or str(value).strip() == "": return "-" + number = as_int(value) + if number < 0: + return "-" + if number == 0: + return "0B" if number >= 1_000_000: return f"{number / 1_000_000:.1f}MB".rstrip("0").rstrip(".") if number >= 1_000: @@ -478,7 +482,8 @@ def request_row_cells(row: dict[str, Any], text_limits: dict[str, int] | None = short_text(row["model"] or row["requested_model"] or row["forwarded_model"], limits.get("model", 16)), row["reasoning_tokens"] if row["reasoning_tokens"] is not None else "-", short_text(request_usage_summary(row), limits.get("usage", 24)), - format_bytes(row["response_bytes_received"]), + format_bytes(row.get("request_body_bytes")), + format_bytes(row.get("response_bytes_received")), request_chunk_progress(row), format_duration_ms_as_seconds(row["first_response_delay_ms"]), format_duration_ms_as_seconds(row["duration_ms"]), @@ -781,8 +786,8 @@ def render_request_detail(row: dict[str, Any]) -> str: f"attempts {row['upstream_attempt_count'] or 0}", f"first {format_duration_ms_as_seconds(row['first_response_delay_ms'])}", f"duration {format_duration_ms_as_seconds(row['duration_ms'])}", - f"request {format_bytes(row['request_body_bytes'])}", - f"response {format_bytes(row['response_bytes_received'])}", + f"request {format_bytes(row.get('request_body_bytes'))}", + f"response {format_bytes(row.get('response_bytes_received'))}", f"chunks {row['stream_chunk_count'] or 0}", f"life {request_lifecycle_label(row)}", f"finished {short_time(row['finished_at'])}", @@ -1217,6 +1222,7 @@ def run_textual( "model": 16, "reasoning": 10, "usage": 36, + "req": 10, "resp": 10, "chunks": 14, "first": 8, @@ -1234,6 +1240,7 @@ def run_textual( "model": 24, "reasoning": 10, "usage": 72, + "req": 12, "resp": 12, "chunks": 16, "first": 8, @@ -1273,6 +1280,7 @@ 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"), "chunks": requests.add_column("Chunks", width=width_profile["chunks"], key="chunks"), "first": requests.add_column("First", width=width_profile["first"], key="first"), @@ -1785,7 +1793,7 @@ def print_once(snapshot: dict[str, Any], filter_text: str = "") -> None: 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"{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} {request_chunk_progress(row):<14} {format_duration_ms_as_seconds(row['duration_ms']):<8} {request_updated_elapsed(row):<8} " + 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)}" ) except BrokenPipeError: diff --git a/tests/test_payload.py b/tests/test_payload.py index 463a697..a5cde6f 100644 --- a/tests/test_payload.py +++ b/tests/test_payload.py @@ -126,6 +126,7 @@ class CodexRetryGatewayTUITests(unittest.TestCase): "input_tokens": 1200, "output_tokens": 340, "cached_tokens": 128, + "request_body_bytes": 0, "response_bytes_received": 363800, "response_stream": True, "stream_chunk_count": 587, @@ -137,11 +138,44 @@ class CodexRetryGatewayTUITests(unittest.TestCase): } 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") 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[14]), len(wide[14])) + self.assertLess(len(compact[15]), len(wide[15])) + + def test_request_row_cells_show_request_bytes(self) -> None: + mod = load_module() + row = { + "seq": 1, + "request_id": "req_1", + "response_id": "resp_1", + "thread_id": "thread_1", + "started_at": "2026-06-30T12:00:00Z", + "status_code": 200, + "path": "/responses", + "model": "gpt-5", + "requested_model": "", + "forwarded_model": "", + "reasoning_tokens": 7, + "input_tokens": 20, + "output_tokens": 10, + "cached_tokens": 5, + "request_body_bytes": 1536, + "response_bytes_received": 4096, + "response_stream": True, + "stream_chunk_count": 4, + "first_response_delay_ms": 1200, + "duration_ms": 3400, + "upstream_attempt_count": 1, + "error": "", + "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") def test_request_rows_read_usage_from_nested_usage_object(self) -> None: mod = load_module()