feat(console): TPS instead of TPOT, agent-turns column reorder, sidebar logo#20
Conversation
…ar logo
Three small UI changes batched into one branch — none of them touch
backend or data shapes:
* Overview "Avg TPOT" KPI surfaces as "Avg TPS" with units of tok/s
(= 1000 / tpot_avg_ms). TPOT itself is what the backend stores;
the conversion is one division at render time. "Generation speed"
reads better in a glance than "milliseconds per token".
* Models table column "TPOT" → "Generation TPS", same unit swap.
Sort key still points at tpot_avg under the hood but getSortValue
inverts to 1000/tpot_avg so clicking the column desc gives
fastest-first — matches what someone clicking "Generation TPS"
expects.
* Agent Turns table column order rewritten around how operators
actually triage a turn: Time, Agent, Client, Calls, Status, In,
Out, then the less-frequently-scanned dimensions (Model, Wire
API, Server, Duration) and the long User Input preview last.
* New TokenScope brand mark replaces the bare panel-toggle button
at the top-left of the sidebar:
- Expanded: wordmark on the left, collapse button on the right.
- Collapsed: icon-only mark; click toggles to expand (the icon
doubles as the expand affordance — discoverable, saves a row).
Both variants share the same glyph (rounded "scope" frame
containing three decreasing token bars) so they line up
visually as the sidebar opens/closes. Stroke uses currentColor
for dark-mode and theme inheritance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # console/src/components/layout/sidebar.tsx
There was a problem hiding this comment.
Now I have enough context. Let me review the PR.
Summary
This PR batches three UI-only changes: (1) surface TPOT as "Generation TPS" (tokens/second = 1000/tpot_avg_ms) in overview KPI and models table, (2) reorder agent-turns columns for operator triage workflow, and (3) add a TokenScope brand logo to the sidebar. No backend changes, no schema changes. The TPS conversion is render-time only and guards against null/zero division. APPROVE — these are clean, low-risk UI polish changes with defensive edge-case handling.
Suggestions
-
console/src/pages/models.tsx:34-36 — The inverted sort (returning
1000/tpot_avgfor the sort value) creates a subtle UX inconsistency: "TTFT avg" and "E2E avg" columns sort by raw ms values (desc = slower), while "Generation TPS" sorts by inverted value (desc = faster). This matches what a user clicking "TPS" expects (higher = faster), but differs from the latency-column convention. Consider documenting this intentional divergence in a comment for future maintainers. -
console/src/components/ui/logo.tsx:58-68 — The wordmark uses SVG
<text>with a system font stack (ui-sans-serif, system-ui, ...). The comment acknowledges cross-OS variance is acceptable, but note thatfontWeight={600}(semi-bold) may render differently across Windows/macOS/Linux — on some systems it may look bolder or lighter than intended. If brand consistency matters, consider shipping a webfont or using a pre-rendered path instead of<text>. -
console/src/pages/agent-turns.tsx:17-21 — The comment explaining column ordering is helpful, but could be slightly clearer: "Identity columns (Agent / Client)" — Agent is technically
agent_kind(the agent type/profile), not the agent identity. Consider "Agent kind / Client" for precision.
Verified
- Schema mirror: Checked
MetricsSummary.tpot_avg(line 48) andMetricsModelRow.tpot_avg(line 80) inconsole/src/types/api.ts— both arenumber | null, matching RustOption<f64>ints-storage-duckdb/src/metrics.rs:500, 645. - Null/zero guards: All TPS conversions check
tpot_avg != null && tpot_avg > 0before dividing (overview:104-107, models:34-36, models:207-209). Prevents NaN/Infinity for edge cases. - No queryKey drift: The TPS change is render-time only;
useMetricsSummaryanduseModelshooks have correct queryKey entries (start,end, filter params). No new API calls. - No route changes: All three files are page/component modifications — no new routes.
app.tsxroute registration unchanged. - Logo component: New file follows project conventions (Tailwind via
cn(),@/lib/utilsimport). Exported correctly, imported only bysidebar.tsx:17. - Sidebar behavior: Toggle button logic uses existing
useSidebarStorecorrectly. Expanded/collapsed states render different button arrangements but both call the sametoggle()function. - Sort consistency: Reviewed models.tsx sort logic —
getSortValueinverts tpot_avg for "Generation TPS" column; sorting bybv - avon desc correctly shows fastest-first when clicking the TPS header.
🤖 Reviewed by vivi • workflow run
Three batched UI tweaks, no backend changes.
1. `Avg TPOT` → `Avg TPS` on Overview, and Models table
TPOT (time per output token, ms) is what the backend stores. Operators want to read generation speed — `1000 / tpot_avg` = tokens/sec — which is more intuitive at a glance. Render-time conversion only; no schema changes.
The Models column still sorts by the underlying `tpot_avg` field, but `getSortValue` inverts to `1000 / tpot_avg` so clicking the header desc puts the fastest models first — matches what someone clicking "Generation TPS" expects.
2. Agent Turns column order
Rewritten around how operators triage a turn — identity first, then shape, then dimensions:
```
Time → Agent → Client → Calls → Status → In → Out → Model → Wire API → Server → Duration → User Input
```
Was previously `Time → Wire API → Model → Agent → Client → Server → Status → Calls → ...`, which buried Agent and Client behind two less-useful columns.
3. Sidebar logo
Replaces the bare panel-toggle button at the top-left of the sidebar with a TokenScope brand mark.
Verification
Test plan
🤖 Generated with Claude Code