feat: improve retry first column controls
This commit is contained in:
+121
-56
@@ -81,6 +81,51 @@ REQUEST_TABLE_COLUMN_KEY_SET = set(REQUEST_TABLE_COLUMN_KEYS)
|
||||
REQUEST_TABLE_CELL_INDEX = {key: index for index, key in enumerate(REQUEST_TABLE_COLUMN_KEYS)}
|
||||
DEFAULT_REQUEST_TABLE_VISIBLE_COLUMNS = REQUEST_TABLE_COLUMN_KEYS
|
||||
DEFAULT_REQUEST_TABLE_SORT_COLUMN = "seq"
|
||||
REQUEST_TABLE_WIDTH_PROFILES = {
|
||||
"compact": {
|
||||
"seq": 7,
|
||||
"req_id": 18,
|
||||
"resp_id": 18,
|
||||
"thread": 18,
|
||||
"started": 19,
|
||||
"status": 7,
|
||||
"path": 22,
|
||||
"model": 16,
|
||||
"effort": 8,
|
||||
"reasoning": 8,
|
||||
"token_col": 8,
|
||||
"req": 10,
|
||||
"resp": 10,
|
||||
"chunks": 14,
|
||||
"first": 12,
|
||||
"duration": 8,
|
||||
"updated": 8,
|
||||
"note": 22,
|
||||
"round": 10,
|
||||
},
|
||||
"wide": {
|
||||
"seq": 7,
|
||||
"req_id": 24,
|
||||
"resp_id": 24,
|
||||
"thread": 36,
|
||||
"started": 19,
|
||||
"status": 7,
|
||||
"path": 40,
|
||||
"model": 24,
|
||||
"effort": 8,
|
||||
"reasoning": 8,
|
||||
"token_col": 8,
|
||||
"req": 12,
|
||||
"resp": 12,
|
||||
"chunks": 16,
|
||||
"first": 14,
|
||||
"duration": 8,
|
||||
"updated": 8,
|
||||
"note": 48,
|
||||
"round": 10,
|
||||
},
|
||||
}
|
||||
REQUEST_TABLE_MIN_COLUMN_WIDTH = 4
|
||||
REQUEST_TABLE_COLUMN_ALIASES = {
|
||||
"seq": ("seq",),
|
||||
"req_id": ("req id", "request id", "request_id"),
|
||||
@@ -295,6 +340,29 @@ def request_table_hidden_columns(value: Any, columns: list[str]) -> list[str]:
|
||||
return hidden
|
||||
|
||||
|
||||
def request_table_column_width_value(value: Any) -> int | None:
|
||||
try:
|
||||
width = int(str(value or "").strip())
|
||||
except Exception:
|
||||
return None
|
||||
if width <= 0:
|
||||
return None
|
||||
return max(REQUEST_TABLE_MIN_COLUMN_WIDTH, width)
|
||||
|
||||
|
||||
def request_table_width_overrides(value: Any) -> dict[str, int]:
|
||||
requested = value if isinstance(value, dict) else {}
|
||||
overrides: dict[str, int] = {}
|
||||
for raw_key, raw_width in requested.items():
|
||||
width = request_table_column_width_value(raw_width)
|
||||
if width is None:
|
||||
continue
|
||||
for column_key in request_table_column_keys(raw_key):
|
||||
if column_key:
|
||||
overrides[column_key] = width
|
||||
return overrides
|
||||
|
||||
|
||||
def normalize_request_table_sort_column(value: Any) -> str:
|
||||
column_key = str(value or "").strip()
|
||||
if column_key in REQUEST_TABLE_COLUMN_KEY_SET:
|
||||
@@ -320,9 +388,11 @@ def normalize_request_table_preferences(data: Any) -> dict[str, Any]:
|
||||
hidden_columns = request_table_hidden_columns(source.get("hidden_columns"), columns)
|
||||
if len(hidden_columns) >= len(columns) and columns:
|
||||
hidden_columns = [column_key for column_key in hidden_columns if column_key != columns[0]]
|
||||
width_overrides = request_table_width_overrides(source.get("widths"))
|
||||
return {
|
||||
"columns": columns,
|
||||
"hidden_columns": hidden_columns,
|
||||
"widths": {column_key: width for column_key, width in width_overrides.items() if column_key in columns},
|
||||
}
|
||||
|
||||
|
||||
@@ -338,6 +408,7 @@ def request_table_visible_columns(preferences: dict[str, Any]) -> list[str]:
|
||||
def request_table_preferences_from_text(text: str) -> dict[str, Any]:
|
||||
columns = []
|
||||
hidden_columns = []
|
||||
width_overrides: dict[str, int] = {}
|
||||
seen: set[str] = set()
|
||||
for line_number, raw_line in enumerate(text.splitlines(), start=1):
|
||||
stripped = raw_line.strip()
|
||||
@@ -350,6 +421,11 @@ def request_table_preferences_from_text(text: str) -> dict[str, Any]:
|
||||
token = token[1:].strip()
|
||||
if not token:
|
||||
continue
|
||||
width = None
|
||||
width_match = re.match(r"^(.*?)(?:\s+(\d+))?$", token)
|
||||
if width_match:
|
||||
token = (width_match.group(1) or "").strip()
|
||||
width = request_table_column_width_value(width_match.group(2))
|
||||
column_keys = request_table_column_keys(token)
|
||||
if not column_keys:
|
||||
if hidden:
|
||||
@@ -362,6 +438,8 @@ def request_table_preferences_from_text(text: str) -> dict[str, Any]:
|
||||
columns.append(column_key)
|
||||
if hidden:
|
||||
hidden_columns.append(column_key)
|
||||
if width is not None:
|
||||
width_overrides[column_key] = width
|
||||
if not columns:
|
||||
raise ValueError("no request columns found in editor document")
|
||||
missing = [column_key for column_key in REQUEST_TABLE_COLUMN_KEYS if column_key not in seen]
|
||||
@@ -369,6 +447,7 @@ def request_table_preferences_from_text(text: str) -> dict[str, Any]:
|
||||
{
|
||||
"columns": columns + missing,
|
||||
"hidden_columns": hidden_columns + missing,
|
||||
"widths": width_overrides,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -376,17 +455,20 @@ def request_table_preferences_from_text(text: str) -> dict[str, Any]:
|
||||
def request_table_preferences_document(preferences: dict[str, Any]) -> str:
|
||||
normalized = normalize_request_table_preferences(preferences)
|
||||
hidden = set(normalized["hidden_columns"])
|
||||
widths = normalized["widths"]
|
||||
lines = [
|
||||
"# Request table columns.",
|
||||
"# One column key per line.",
|
||||
"# Prefix with # to hide a column.",
|
||||
"# Optional: append an integer width, e.g. `first 14` or `in 10`.",
|
||||
"# Move lines up or down to change display order.",
|
||||
"# Common keys: in, out, cache, req_id, resp_id, req_bytes, resp_bytes, round.",
|
||||
"",
|
||||
]
|
||||
for column_key in normalized["columns"]:
|
||||
prefix = "# " if column_key in hidden else ""
|
||||
lines.append(f"{prefix}{column_key}")
|
||||
width_suffix = f" {widths[column_key]}" if column_key in widths else ""
|
||||
lines.append(f"{prefix}{column_key}{width_suffix}")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
@@ -769,6 +851,7 @@ def normalize_request_retry_firsts(value: Any) -> list[dict[str, Any]]:
|
||||
continue
|
||||
firsts.append(
|
||||
{
|
||||
"round": parse_int_value(item.get("round")),
|
||||
"slot": parse_int_value(item.get("slot")),
|
||||
"first_delay_ms": item.get("first_response_delay_ms", item.get("first_delay_ms", item.get("first_ms"))),
|
||||
"outcome": str(item.get("outcome") or "").strip(),
|
||||
@@ -781,6 +864,20 @@ def normalize_request_retry_firsts(value: Any) -> list[dict[str, Any]]:
|
||||
return firsts
|
||||
|
||||
|
||||
def request_retry_first_label(first: dict[str, Any], fallback_round: Any = None) -> str:
|
||||
round_number = parse_int_value(first.get("round"))
|
||||
if round_number is None:
|
||||
round_number = parse_int_value(fallback_round)
|
||||
slot = parse_int_value(first.get("slot"))
|
||||
if round_number is not None:
|
||||
if slot is not None and slot > 1:
|
||||
return f"{round_number}-{slot}"
|
||||
return str(round_number)
|
||||
if slot is not None:
|
||||
return str(slot)
|
||||
return "?"
|
||||
|
||||
|
||||
def request_retry_round_text(row: dict[str, Any]) -> str:
|
||||
round_number = parse_int_value(row.get("reasoning_retry_current_round"))
|
||||
width = parse_int_value(row.get("reasoning_retry_current_width"))
|
||||
@@ -793,8 +890,11 @@ def request_retry_round_text(row: dict[str, Any]) -> str:
|
||||
return f"{round_number}({max(0, width)})"
|
||||
|
||||
|
||||
def request_retry_first_summary(first: dict[str, Any]) -> str:
|
||||
def request_retry_first_summary(first: dict[str, Any], fallback_round: Any = None) -> str:
|
||||
bits = []
|
||||
round_label = request_retry_first_label(first, fallback_round)
|
||||
if round_label != "?":
|
||||
bits.append(f"round {round_label}")
|
||||
slot = parse_int_value(first.get("slot"))
|
||||
if slot is not None:
|
||||
bits.append(f"slot {slot}")
|
||||
@@ -823,10 +923,11 @@ def request_retry_first_summary(first: dict[str, Any]) -> str:
|
||||
|
||||
def request_retry_wave_summary(row: dict[str, Any]) -> str:
|
||||
summaries = []
|
||||
fallback_round = row.get("reasoning_retry_current_round")
|
||||
for first in row.get("reasoning_retry_current_firsts") or []:
|
||||
if not isinstance(first, dict):
|
||||
continue
|
||||
summary = request_retry_first_summary(first)
|
||||
summary = request_retry_first_summary(first, fallback_round)
|
||||
if summary:
|
||||
summaries.append(summary)
|
||||
return "; ".join(summaries)
|
||||
@@ -838,10 +939,7 @@ def request_retry_firsts_compact_text(row: dict[str, Any]) -> str:
|
||||
if not isinstance(first, dict):
|
||||
continue
|
||||
delay = format_duration_ms_as_seconds(first.get("first_delay_ms"))
|
||||
if delay == "-":
|
||||
delay = str(first.get("outcome") or "-").strip() or "-"
|
||||
slot = parse_int_value(first.get("slot"))
|
||||
parts.append(f"{slot if slot is not None else '?'}:{delay}")
|
||||
parts.append(delay if delay != "-" else "?")
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
@@ -1744,48 +1842,7 @@ def run_textual(
|
||||
self.request_table_preferences = load_request_table_preferences()
|
||||
self.request_table_density = "compact"
|
||||
self.request_table_width_profiles = {
|
||||
"compact": {
|
||||
"seq": 7,
|
||||
"req_id": 18,
|
||||
"resp_id": 18,
|
||||
"thread": 18,
|
||||
"started": 19,
|
||||
"status": 7,
|
||||
"path": 22,
|
||||
"model": 16,
|
||||
"effort": 8,
|
||||
"reasoning": 8,
|
||||
"token_col": 8,
|
||||
"req": 10,
|
||||
"resp": 10,
|
||||
"chunks": 14,
|
||||
"first": 8,
|
||||
"duration": 8,
|
||||
"updated": 8,
|
||||
"note": 22,
|
||||
"round": 10,
|
||||
},
|
||||
"wide": {
|
||||
"seq": 7,
|
||||
"req_id": 24,
|
||||
"resp_id": 24,
|
||||
"thread": 36,
|
||||
"started": 19,
|
||||
"status": 7,
|
||||
"path": 40,
|
||||
"model": 24,
|
||||
"effort": 8,
|
||||
"reasoning": 8,
|
||||
"token_col": 8,
|
||||
"req": 12,
|
||||
"resp": 12,
|
||||
"chunks": 16,
|
||||
"first": 8,
|
||||
"duration": 8,
|
||||
"updated": 8,
|
||||
"note": 48,
|
||||
"round": 10,
|
||||
},
|
||||
density: dict(widths) for density, widths in REQUEST_TABLE_WIDTH_PROFILES.items()
|
||||
}
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
@@ -1875,6 +1932,16 @@ def run_textual(
|
||||
return False
|
||||
return True
|
||||
|
||||
def _request_table_column_width(self, column_key: str, density: str | None = None) -> int:
|
||||
normalized = normalize_request_table_preferences(self.request_table_preferences)
|
||||
override = request_table_column_width_value(normalized.get("widths", {}).get(column_key))
|
||||
if override is not None:
|
||||
return override
|
||||
active_density = density or self.request_table_density
|
||||
width_profile = self.request_table_width_profiles[active_density]
|
||||
column = REQUEST_TABLE_COLUMN_BY_KEY[column_key]
|
||||
return width_profile[column["width_key"]]
|
||||
|
||||
def _request_row_key(self, row: dict[str, Any]) -> str:
|
||||
return f"{row['seq']}:{row['request_id']}"
|
||||
|
||||
@@ -1897,12 +1964,11 @@ def run_textual(
|
||||
table = self.query_one("#requests_table", DataTable)
|
||||
selected_row_index = table.cursor_row if table.cursor_row is not None and table.cursor_row >= 0 else 0
|
||||
selected_row_key = self._selected_request_key()
|
||||
width_profile = self.request_table_width_profiles[self.request_table_density]
|
||||
table.clear(columns=True)
|
||||
self.request_table_column_keys = {}
|
||||
for column_key in self._visible_request_column_keys():
|
||||
column = REQUEST_TABLE_COLUMN_BY_KEY[column_key]
|
||||
width = width_profile[column["width_key"]]
|
||||
width = self._request_table_column_width(column_key)
|
||||
self.request_table_column_keys[column_key] = table.add_column(
|
||||
column["label"],
|
||||
width=width,
|
||||
@@ -2140,10 +2206,11 @@ def run_textual(
|
||||
"path": width_profile["path"],
|
||||
"model": width_profile["model"],
|
||||
"effort": width_profile["effort"],
|
||||
"first": width_profile["first"],
|
||||
"note": width_profile["note"],
|
||||
"round": width_profile["round"],
|
||||
}
|
||||
for name in ("req_id", "resp_id", "thread", "path", "model", "effort", "note", "round"):
|
||||
for name in ("req_id", "resp_id", "thread", "path", "model", "effort", "first", "note", "round"):
|
||||
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):
|
||||
@@ -2164,13 +2231,11 @@ def run_textual(
|
||||
|
||||
def _apply_request_table_width_profile(self, density: str) -> None:
|
||||
table = self.query_one("#requests_table", DataTable)
|
||||
width_profile = self.request_table_width_profiles[density]
|
||||
for name, key in self.request_table_column_keys.items():
|
||||
column = table.columns.get(key)
|
||||
width_key = REQUEST_TABLE_COLUMN_BY_KEY.get(name, {}).get("width_key", name)
|
||||
if column is None or width_key not in width_profile:
|
||||
if column is None:
|
||||
continue
|
||||
column.width = width_profile[width_key]
|
||||
column.width = self._request_table_column_width(name, density)
|
||||
self.request_table_density = density
|
||||
self._rerender_requests_after_layout_change()
|
||||
self._set_status(f"request table mode {density}")
|
||||
|
||||
+11
-5
@@ -230,6 +230,7 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
"reasoning_retry_current_width": 2,
|
||||
"reasoning_retry_current_firsts": [
|
||||
{
|
||||
"round": 3,
|
||||
"slot": 1,
|
||||
"first_response_delay_ms": 1200,
|
||||
"outcome": "ok",
|
||||
@@ -238,6 +239,7 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
"reasoning_tokens": 516,
|
||||
},
|
||||
{
|
||||
"round": 3,
|
||||
"slot": 2,
|
||||
"first_delay_ms": 1800,
|
||||
"outcome": "retry",
|
||||
@@ -254,10 +256,11 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
self.assertEqual(row["reasoning_retry_current_width"], 2)
|
||||
self.assertEqual(len(row["reasoning_retry_current_firsts"]), 2)
|
||||
self.assertEqual(mod.request_retry_round_text(row), "3(2)")
|
||||
self.assertIn("1:1.2s", mod.request_first_text(row))
|
||||
self.assertIn("slot 1", mod.request_retry_wave_summary(row))
|
||||
self.assertEqual(mod.request_first_text(row), "1.2s 1.8s")
|
||||
self.assertIn("round 3 slot 1", mod.request_retry_wave_summary(row))
|
||||
self.assertIn("retry round 3(2)", mod.render_request_detail(row))
|
||||
self.assertIn("current wave slot 1 first 1.2s", mod.render_request_detail(row))
|
||||
self.assertIn("first 1.2s 1.8s", mod.render_request_detail(row))
|
||||
self.assertIn("current wave round 3 slot 1 first 1.2s", mod.render_request_detail(row))
|
||||
self.assertEqual(mod.request_row_cells(row)[20], "3(2)")
|
||||
|
||||
def test_request_table_preferences_round_trip(self) -> None:
|
||||
@@ -273,12 +276,14 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
{
|
||||
"columns": ["round", "seq", "status", "unknown"],
|
||||
"hidden_columns": ["status", "unknown"],
|
||||
"widths": {"round": 14, "status": 9, "unknown": 99},
|
||||
}
|
||||
)
|
||||
self.assertEqual(saved_path, prefs_path)
|
||||
loaded = mod.load_request_table_preferences()
|
||||
self.assertEqual(loaded["columns"][:3], ["round", "seq", "status"])
|
||||
self.assertEqual(loaded["hidden_columns"], ["status"])
|
||||
self.assertEqual(loaded["widths"], {"round": 14, "status": 9})
|
||||
|
||||
def test_request_table_preferences_from_text_hides_and_reorders_columns(self) -> None:
|
||||
mod = load_module()
|
||||
@@ -286,9 +291,9 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
"\n".join(
|
||||
[
|
||||
"# comment",
|
||||
"thread",
|
||||
"thread 24",
|
||||
"seq",
|
||||
"# cache",
|
||||
"# cache 9",
|
||||
"in",
|
||||
"out",
|
||||
"",
|
||||
@@ -297,6 +302,7 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(prefs["columns"][:6], ["thread", "seq", "cache", "in", "out", "req_id"])
|
||||
self.assertIn("cache", prefs["hidden_columns"])
|
||||
self.assertEqual(prefs["widths"], {"thread": 24, "cache": 9})
|
||||
|
||||
def test_request_table_preferences_expand_legacy_usage_column(self) -> None:
|
||||
mod = load_module()
|
||||
|
||||
Reference in New Issue
Block a user