feat: reorder first/latency in seconds, add effort column

- logs columns are now Key | Account | Model | Effort | Type | Tokens |
  Cost | First | Latency | Tok/s | Time
- First and Latency render in seconds (5.3s) instead of milliseconds
- Effort shows the request reasoning_effort (- when absent) and is
  filterable; the detail line appends it to the model as model (effort)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 11:45:19 +08:00
parent 2d3d433f4f
commit fd4e3256d4
5 changed files with 53 additions and 20 deletions
+22 -9
View File
@@ -18,7 +18,7 @@ from typing import Any
APP_NAME = "shusub2"
FALLBACK_VERSION = "0.2.1"
FALLBACK_VERSION = "0.2.2"
DEFAULT_API_URL = "http://127.0.0.1:18318/api/tui/accounts"
DEFAULT_CONFIG_FILE = "~/.config/shusub2/api-url"
DEFAULT_STATUS_CONFIG_FILE = "~/.config/shusub2/status-url"
@@ -369,6 +369,13 @@ def format_rate(value: Any) -> str:
return f"{rate:.1f}/s"
def format_seconds(value: Any) -> str:
number = as_int(value)
if number <= 0:
return "-"
return f"{number / 1000:.1f}s"
KEY_COLOR_PALETTE = (
"cyan",
"magenta",
@@ -413,6 +420,7 @@ def normalize_log_rows(payload: dict[str, Any], filter_text: str = "") -> list[d
user_name = nested_name(item, "user", "user_id", "user_name")
model = str(item.get("model") or "-")
upstream_model = str(item.get("upstream_model") or "").strip()
effort = str(item.get("reasoning_effort") or "").strip() or "-"
type_label = log_type_label(item)
row = {
"id": as_int(item.get("id")),
@@ -421,6 +429,7 @@ def normalize_log_rows(payload: dict[str, Any], filter_text: str = "") -> list[d
"user": user_name,
"model": model,
"upstream_model": upstream_model,
"effort": effort,
"type": type_label,
"tokens": log_total_tokens(item),
"input_tokens": as_int(item.get("input_tokens")),
@@ -439,7 +448,7 @@ def normalize_log_rows(payload: dict[str, Any], filter_text: str = "") -> list[d
}
if needle:
haystack = " ".join(
(key_name, account_name, user_name, model, upstream_model, type_label, row["request_id"])
(key_name, account_name, user_name, model, upstream_model, effort, type_label, row["request_id"])
).lower()
if needle not in haystack:
continue
@@ -465,12 +474,14 @@ def log_detail_line(row: dict[str, Any]) -> str:
model = row["model"]
if row["upstream_model"] and row["upstream_model"] != row["model"]:
model = f"{row['model']} -> {row['upstream_model']}"
if row["effort"] != "-":
model = f"{model} ({row['effort']})"
detail = (
f"{row['time']} | key {row['key']} | account {row['account']} | user {row['user']} | {model} | {row['type']} | "
f"tokens in {format_count(row['input_tokens'])} out {format_count(row['output_tokens'])} "
f"cache_w {format_count(row['cache_creation_tokens'])} cache_r {format_count(row['cache_read_tokens'])} | "
f"cost {format_cost(row['cost'])} (actual {format_cost(row['actual_cost'])}) | "
f"latency {format_latency(row['duration_ms'])} first {format_latency(row['first_token_ms'])} "
f"first {format_seconds(row['first_token_ms'])} latency {format_seconds(row['duration_ms'])} "
f"{format_rate(row['tokens_per_second'])}"
)
if row["request_id"]:
@@ -481,17 +492,18 @@ def log_detail_line(row: dict[str, Any]) -> str:
def print_logs_once(payload: dict[str, Any], filter_text: str = "") -> None:
rows = normalize_log_rows(payload, filter_text)
print(logs_summary_line(payload, len(rows)))
print("key account model type tokens cost latency first tok/s time")
print("key account model effort type tokens cost first latency tok/s time")
for row in rows:
print(
f"{row['key'][:20]:<21} "
f"{row['account'][:20]:<21} "
f"{row['model'][:24]:<25} "
f"{row['effort']:<7} "
f"{row['type']:<7} "
f"{format_count(row['tokens']):<8} "
f"{format_cost(row['cost']):<10} "
f"{format_latency(row['duration_ms']):<9} "
f"{format_latency(row['first_token_ms']):<9} "
f"{format_seconds(row['first_token_ms']):<8} "
f"{format_seconds(row['duration_ms']):<8} "
f"{format_rate(row['tokens_per_second']):<8} "
f"{row['time']}"
)
@@ -927,7 +939,7 @@ def run_textual(
table = self.query_one("#logs", DataTable)
table.cursor_type = "row"
table.zebra_stripes = True
table.add_columns("Key", "Account", "Model", "Type", "Tokens", "Cost", "Latency", "First", "Tok/s", "Time")
table.add_columns("Key", "Account", "Model", "Effort", "Type", "Tokens", "Cost", "First", "Latency", "Tok/s", "Time")
self.refresh_data()
self.set_interval(logs_refresh_seconds, self.refresh_data)
@@ -976,11 +988,12 @@ def run_textual(
Text(row["key"], style=color) if color else row["key"],
row["account"],
row["model"],
row["effort"],
row["type"],
format_count(row["tokens"]),
format_cost(row["cost"]),
format_latency(row["duration_ms"]),
format_latency(row["first_token_ms"]),
format_seconds(row["first_token_ms"]),
format_seconds(row["duration_ms"]),
format_rate(row["tokens_per_second"]),
row["time"],
key=key,