feat: color requests by thread id
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
import argparse
|
import argparse
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
import hashlib
|
||||||
import importlib.metadata
|
import importlib.metadata
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -81,6 +82,19 @@ 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)}
|
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_VISIBLE_COLUMNS = REQUEST_TABLE_COLUMN_KEYS
|
||||||
DEFAULT_REQUEST_TABLE_SORT_COLUMN = "seq"
|
DEFAULT_REQUEST_TABLE_SORT_COLUMN = "seq"
|
||||||
|
THREAD_COLOR_PALETTE = (
|
||||||
|
"#5fd7ff",
|
||||||
|
"#87d75f",
|
||||||
|
"#ffaf5f",
|
||||||
|
"#d787ff",
|
||||||
|
"#5fafff",
|
||||||
|
"#ff87d7",
|
||||||
|
"#d7af5f",
|
||||||
|
"#5fd7af",
|
||||||
|
"#af87ff",
|
||||||
|
"#87afd7",
|
||||||
|
)
|
||||||
|
THREAD_COLOR_COLUMN_KEYS = frozenset({"req_id", "resp_id", "thread"})
|
||||||
REQUEST_TABLE_WIDTH_PROFILES = {
|
REQUEST_TABLE_WIDTH_PROFILES = {
|
||||||
"compact": {
|
"compact": {
|
||||||
"seq": 7,
|
"seq": 7,
|
||||||
@@ -990,6 +1004,37 @@ def request_row_cells(row: dict[str, Any], text_limits: dict[str, int] | None =
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def request_thread_color(thread_id: Any) -> str | None:
|
||||||
|
text = str(thread_id or "").strip()
|
||||||
|
if not text:
|
||||||
|
return None
|
||||||
|
digest = hashlib.blake2b(text.encode("utf-8"), digest_size=4).digest()
|
||||||
|
color_index = int.from_bytes(digest[:2], "big") % len(THREAD_COLOR_PALETTE)
|
||||||
|
return THREAD_COLOR_PALETTE[color_index]
|
||||||
|
|
||||||
|
|
||||||
|
def request_visible_cells(
|
||||||
|
row: dict[str, Any],
|
||||||
|
cells: tuple[Any, ...],
|
||||||
|
visible_column_keys: list[str],
|
||||||
|
) -> list[Any]:
|
||||||
|
visible_cells = [cells[REQUEST_TABLE_CELL_INDEX[column_key]] for column_key in visible_column_keys]
|
||||||
|
thread_color = request_thread_color(row.get("thread_id"))
|
||||||
|
if not thread_color:
|
||||||
|
return visible_cells
|
||||||
|
try:
|
||||||
|
from rich.text import Text
|
||||||
|
except ImportError:
|
||||||
|
return visible_cells
|
||||||
|
styled_cells: list[Any] = []
|
||||||
|
for column_key, cell in zip(visible_column_keys, visible_cells):
|
||||||
|
if column_key in THREAD_COLOR_COLUMN_KEYS:
|
||||||
|
styled_cells.append(Text(str(cell), style=f"bold {thread_color}"))
|
||||||
|
else:
|
||||||
|
styled_cells.append(cell)
|
||||||
|
return styled_cells
|
||||||
|
|
||||||
|
|
||||||
def request_table_column_label(column_key: str) -> str:
|
def request_table_column_label(column_key: str) -> str:
|
||||||
column = REQUEST_TABLE_COLUMN_BY_KEY.get(column_key)
|
column = REQUEST_TABLE_COLUMN_BY_KEY.get(column_key)
|
||||||
return str(column["label"]) if column else column_key
|
return str(column["label"]) if column else column_key
|
||||||
@@ -2113,13 +2158,14 @@ def run_textual(
|
|||||||
selected_row_index = table.cursor_row if table.cursor_row is not None and table.cursor_row >= 0 else 0
|
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()
|
selected_row_key = self._selected_request_key()
|
||||||
text_limits = self._request_text_limits(table)
|
text_limits = self._request_text_limits(table)
|
||||||
|
visible_column_keys = self._visible_request_column_keys()
|
||||||
table.clear()
|
table.clear()
|
||||||
self.request_by_key = {}
|
self.request_by_key = {}
|
||||||
for row in self.requests:
|
for row in self.requests:
|
||||||
key = self._request_row_key(row)
|
key = self._request_row_key(row)
|
||||||
self.request_by_key[key] = row
|
self.request_by_key[key] = row
|
||||||
cells = request_row_cells(row, text_limits)
|
cells = request_row_cells(row, text_limits)
|
||||||
visible_cells = [cells[REQUEST_TABLE_CELL_INDEX[column_key]] for column_key in self._visible_request_column_keys()]
|
visible_cells = request_visible_cells(row, cells, visible_column_keys)
|
||||||
table.add_row(*visible_cells, key=key)
|
table.add_row(*visible_cells, key=key)
|
||||||
if self.requests:
|
if self.requests:
|
||||||
if not self._select_request_row(selected_row_key):
|
if not self._select_request_row(selected_row_key):
|
||||||
|
|||||||
@@ -263,6 +263,51 @@ class CodexRetryGatewayTUITests(unittest.TestCase):
|
|||||||
self.assertIn("current wave round 3 slot 1 first 1.2s", 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)")
|
self.assertEqual(mod.request_row_cells(row)[20], "3(2)")
|
||||||
|
|
||||||
|
def test_request_visible_cells_colorize_thread_identity_columns(self) -> None:
|
||||||
|
mod = load_module()
|
||||||
|
try:
|
||||||
|
from rich.text import Text
|
||||||
|
except ImportError:
|
||||||
|
self.skipTest("rich is not installed")
|
||||||
|
row = {
|
||||||
|
"seq": 1,
|
||||||
|
"request_id": "req_1",
|
||||||
|
"response_id": "resp_1",
|
||||||
|
"thread_id": "thread_same",
|
||||||
|
"started_at": "2026-06-30T12:00:00Z",
|
||||||
|
"status_code": 200,
|
||||||
|
"path": "/responses",
|
||||||
|
"model": "gpt-5",
|
||||||
|
"requested_model": "",
|
||||||
|
"forwarded_model": "",
|
||||||
|
"reasoning_tokens": 7,
|
||||||
|
"reasoning_effort": "xhigh",
|
||||||
|
"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)
|
||||||
|
visible = mod.request_visible_cells(row, cells, ["seq", "req_id", "resp_id", "thread", "status"])
|
||||||
|
self.assertEqual(visible[0], "1")
|
||||||
|
self.assertIsInstance(visible[1], Text)
|
||||||
|
self.assertIsInstance(visible[2], Text)
|
||||||
|
self.assertIsInstance(visible[3], Text)
|
||||||
|
self.assertEqual(visible[1].plain, "req_1")
|
||||||
|
self.assertEqual(visible[2].plain, "resp_1")
|
||||||
|
self.assertEqual(visible[3].plain, "thread_same")
|
||||||
|
self.assertEqual(visible[4], "· 200")
|
||||||
|
self.assertEqual(mod.request_thread_color("thread_same"), mod.request_thread_color("thread_same"))
|
||||||
|
self.assertIsNone(mod.request_thread_color(""))
|
||||||
|
|
||||||
def test_request_table_preferences_round_trip(self) -> None:
|
def test_request_table_preferences_round_trip(self) -> None:
|
||||||
mod = load_module()
|
mod = load_module()
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
|||||||
Reference in New Issue
Block a user