feat: show first-token latency, decode tok/s, per-key colors
- logs page adds First (first_token_ms) and Tok/s columns; Tok/s is output_tokens / (latency - first_token) over the decode window - detail line shows the same rate after first-token latency - API key names render in a stable per-key color (crc32 over a 10-color palette) so rows from the same key group visually; --once stays plain - --once --logs prints the new first/tok-s columns Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -432,6 +432,42 @@ class Sub2APILogsTests(unittest.TestCase):
|
||||
capped = mod.logs_request_url("https://sub2apicn.shujk.top/api/v1/admin/usage", 9000)
|
||||
self.assertIn("page_size=1000", capped)
|
||||
|
||||
def test_log_tokens_per_second_uses_decode_window(self) -> None:
|
||||
mod = load_module()
|
||||
|
||||
item = {"output_tokens": 340, "duration_ms": 5321, "first_token_ms": 800}
|
||||
self.assertAlmostEqual(mod.log_tokens_per_second(item), 340 * 1000.0 / (5321 - 800))
|
||||
self.assertEqual(mod.format_rate(mod.log_tokens_per_second(item)), "75.2/s")
|
||||
# missing first token: falls back to the full duration
|
||||
self.assertAlmostEqual(mod.log_tokens_per_second({"output_tokens": 100, "duration_ms": 2000}), 50.0)
|
||||
# no output, zero duration, or first token >= duration -> no rate
|
||||
self.assertEqual(mod.log_tokens_per_second({"output_tokens": 0, "duration_ms": 2000}), 0.0)
|
||||
self.assertEqual(mod.log_tokens_per_second({"output_tokens": 10, "duration_ms": 0}), 0.0)
|
||||
self.assertEqual(mod.log_tokens_per_second({"output_tokens": 10, "duration_ms": 500, "first_token_ms": 500}), 0.0)
|
||||
self.assertEqual(mod.format_rate(0), "-")
|
||||
self.assertEqual(mod.format_rate(123.4), "123/s")
|
||||
|
||||
def test_normalize_log_rows_carries_first_token_and_rate(self) -> None:
|
||||
mod = load_module()
|
||||
|
||||
rows = mod.normalize_log_rows(sample_logs_payload())
|
||||
oldest = rows[-1]
|
||||
self.assertEqual(oldest["first_token_ms"], 800)
|
||||
self.assertAlmostEqual(oldest["tokens_per_second"], 340 * 1000.0 / 4521)
|
||||
self.assertIn("first 800ms", mod.log_detail_line(oldest))
|
||||
self.assertIn("75.2/s", mod.log_detail_line(oldest))
|
||||
|
||||
def test_key_color_is_stable_and_from_palette(self) -> None:
|
||||
mod = load_module()
|
||||
|
||||
first = mod.key_color("codex-main")
|
||||
self.assertIn(first, mod.KEY_COLOR_PALETTE)
|
||||
self.assertEqual(first, mod.key_color("codex-main"))
|
||||
self.assertEqual(mod.key_color(""), "")
|
||||
self.assertEqual(mod.key_color("-"), "")
|
||||
colors = {mod.key_color(name) for name in ("a", "b", "c", "d", "e", "f")}
|
||||
self.assertGreater(len(colors), 1)
|
||||
|
||||
def test_print_logs_once_renders_requested_columns(self) -> None:
|
||||
mod = load_module()
|
||||
out = io.StringIO()
|
||||
@@ -448,6 +484,10 @@ class Sub2APILogsTests(unittest.TestCase):
|
||||
self.assertIn("5.7K", text)
|
||||
self.assertIn("$0.012", text)
|
||||
self.assertIn("5321ms", text)
|
||||
self.assertIn("first", text)
|
||||
self.assertIn("tok/s", text)
|
||||
self.assertIn("800ms", text)
|
||||
self.assertIn("75.2/s", text)
|
||||
self.assertIn("total 2.3K records", text)
|
||||
|
||||
def test_default_logs_token_reads_config_file(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user