From d3eecf7ba2ce6cb6a5e7fd1cf56ddcc9230ef8de Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 11 Aug 2026 22:39:19 -0400 Subject: [PATCH] feat(tui): compact turn token usage with expandable steps (#41922) --- packages/tui/src/component/devtools-bar.tsx | 22 ++-- packages/tui/src/routes/session/index.tsx | 109 +++++++++++++------- 2 files changed, 84 insertions(+), 47 deletions(-) diff --git a/packages/tui/src/component/devtools-bar.tsx b/packages/tui/src/component/devtools-bar.tsx index 3b1fbe76ab..2212c07aa0 100644 --- a/packages/tui/src/component/devtools-bar.tsx +++ b/packages/tui/src/component/devtools-bar.tsx @@ -384,16 +384,18 @@ export function DevToolsBar() { > {turnTokens() ? "[x]" : "[ ]"} Turn token usage - - void config.update((draft) => { - draft.debug = { ...draft.debug, turn_tokens: verboseTurnTokens() ? true : "verbose" } - }) - } - hoverBackground - > - {verboseTurnTokens() ? "[x]" : "[ ]"} Turn token usage (verbose) - + + + void config.update((draft) => { + draft.debug = { ...draft.debug, turn_tokens: verboseTurnTokens() ? true : "verbose" } + }) + } + hoverBackground + > + {verboseTurnTokens() ? "[x]" : "[ ]"} Turn token usage (verbose) + + {(group) => ( diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index bedc64e1fd..7ed72cec6f 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -1222,6 +1222,11 @@ function TurnTokenUsage(props: { }) { const config = useConfig() const theme = useTheme() + const renderer = useRenderer() + // Collapsed by default: one summary line for the whole turn. Click to + // open the full per-step table, click again to close. + const [expanded, setExpanded] = createSignal(false) + const [hover, setHover] = createSignal(false) const verbose = () => config.data.debug?.turn_tokens === "verbose" const steps = createMemo(() => { let previousCache = props.previousCache @@ -1257,49 +1262,79 @@ function TurnTokenUsage(props: { cached: Math.max("Cached".length, ...steps().map((item) => item.cached.toLocaleString().length)), total: Math.max("Total".length, ...steps().map((item) => item.total.toLocaleString().length)), })) + const summary = createMemo(() => { + const items = steps() + const last = items[items.length - 1] + return { + count: items.length, + newTokens: items.reduce((sum, item) => sum + item.newTokens, 0), + cached: last?.cached ?? 0, + total: last?.total ?? 0, + reuseDrops: items.filter((item) => item.reuseDrop !== undefined).length, + } + }) return ( 0}> - - - ◈ - - - Tokens + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => { + if (renderer.getSelection()?.getSelectedText()) return + setExpanded((value) => !value) + }} + > + + {expanded() ? "- " : "+ "} + Tokens + + : {summary().count} {summary().count === 1 ? "step" : "steps"} ·{" "} + {summary().newTokens.toLocaleString()} new · {summary().cached.toLocaleString()} cached ·{" "} + {summary().total.toLocaleString()} total + + 0}> + + {" "} + · ! {summary().reuseDrops} likely cache {summary().reuseDrops === 1 ? "bust" : "busts"} + + - - - {"Step".padEnd(columns().step + 2)} - {"New".padStart(columns().newTokens)} - {" "} - {"Cached".padStart(columns().cached)} - {" "} - {"Total".padStart(columns().total)} - - - - {(item) => ( - - - {item.finish.padEnd(columns().step + 2)} - - {item.newTokens.toLocaleString().padStart(columns().newTokens)} - - {" "} - {item.cached.toLocaleString().padStart(columns().cached)} - {" "} - {item.total.toLocaleString().padStart(columns().total)} - - - - - ! Likely cache bust: {item.reuseDrop?.toLocaleString()} fewer cached tokens than the previous step + + + + {"Step".padEnd(columns().step + 2)} + {"New".padStart(columns().newTokens)} + {" "} + {"Cached".padStart(columns().cached)} + {" "} + {"Total".padStart(columns().total)} + + + + {(item) => ( + + + {item.finish.padEnd(columns().step + 2)} + + {item.newTokens.toLocaleString().padStart(columns().newTokens)} + + {" "} + {item.cached.toLocaleString().padStart(columns().cached)} + {" "} + {item.total.toLocaleString().padStart(columns().total)} - - - )} - + + + + ! Likely cache bust: {item.reuseDrop?.toLocaleString()} fewer cached tokens than the previous step + + + + )} + + )