From 62841eea35b5ff2fb79e62d852f978bfd0216fe8 Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 20 Feb 2026 21:22:22 +0000 Subject: [PATCH] feat(cli): always show quota remaining in /stats --- .../src/ui/components/ModelStatsDisplay.tsx | 38 +-- .../cli/src/ui/components/StatsDisplay.tsx | 280 +++++++++--------- .../ModelStatsDisplay.test.tsx.snap | 21 ++ .../__snapshots__/StatsDisplay.test.tsx.snap | 93 ++++++ 4 files changed, 260 insertions(+), 172 deletions(-) diff --git a/packages/cli/src/ui/components/ModelStatsDisplay.tsx b/packages/cli/src/ui/components/ModelStatsDisplay.tsx index eec58e9968..0db7955b9b 100644 --- a/packages/cli/src/ui/components/ModelStatsDisplay.tsx +++ b/packages/cli/src/ui/components/ModelStatsDisplay.tsx @@ -65,21 +65,6 @@ export const ModelStatsDisplay: React.FC = ({ ([, metrics]) => metrics.api.totalRequests > 0, ); - if (activeModels.length === 0) { - return ( - - - No API calls have been made in this session. - - - ); - } - const modelNames = activeModels.map(([name]) => name); const hasThoughts = activeModels.some( @@ -354,19 +339,20 @@ export const ModelStatsDisplay: React.FC = ({ {tier} )} - {isAuto && - pooledRemaining !== undefined && - pooledLimit !== undefined && - pooledLimit > 0 && ( - - )} + {(showUserIdentity || isAuto) && } - + {activeModels.length === 0 ? ( + + No API calls have been made in this session. + + ) : ( +
+ )} ); }; diff --git a/packages/cli/src/ui/components/StatsDisplay.tsx b/packages/cli/src/ui/components/StatsDisplay.tsx index 3a50f5b3ad..be4412087e 100644 --- a/packages/cli/src/ui/components/StatsDisplay.tsx +++ b/packages/cli/src/ui/components/StatsDisplay.tsx @@ -162,15 +162,7 @@ const ModelUsageTable: React.FC<{ ? `${getDisplayString(currentModel)} Usage` : `Model Usage`; - const hasPooledQuota = - (pooledRemaining !== undefined && pooledRemaining !== null) || - (pooledLimit !== undefined && pooledLimit !== null && pooledLimit > 0); - - if (rows.length === 0 && !hasPooledQuota && !isAuto) { - return null; - } - - const showQuotaColumn = !!quotas && rows.some((row) => !!row.bucket); + const showQuotaColumn = !!quotas; const nameWidth = 22; const requestsWidth = 7; @@ -202,28 +194,100 @@ const ModelUsageTable: React.FC<{ - {(hasPooledQuota || isAuto) && ( - - - {isAuto && ( - - For a full token breakdown, run `/stats model`. - - )} - - )} + + + {isAuto && ( + + For a full token breakdown, run `/stats model`. + + )} + - {rows.length > 0 && ( - <> - - + + + + Model + + + + + Reqs + + + + {!showQuotaColumn && ( + <> + - Model + Input Tokens + + + + + Cache Reads + + + + + Output Tokens + + + + )} + {showQuotaColumn && ( + + + Usage left + + + )} + + + {/* Divider */} + + + {rows.length > 0 ? ( + rows.map((row) => ( + + + + {row.modelName} - - Reqs + + {row.requests} - {!showQuotaColumn && ( <> - - Input Tokens + + {row.inputTokens} - - Cache Reads - + {row.cachedTokens} - - Output Tokens + + {row.outputTokens} )} - {showQuotaColumn && ( - - - Usage left - - - )} - - - {/* Divider */} - - - {rows.map((row) => ( - - - - {row.modelName} - - - - - {row.requests} - - - {!showQuotaColumn && ( - <> - - - {row.inputTokens} + + {row.bucket && row.bucket.remainingFraction != null && ( + + {(row.bucket.remainingFraction * 100).toFixed(1)}% + {row.bucket.resetTime && ( + + {' '} + ( + {(function (t) { + const formatted = formatResetTime(t); + return formatted === 'Resetting...' || + formatted === '< 1m' + ? formatted + : `Resets in ${formatted}`; + })(row.bucket.resetTime)} + ) - - - {row.cachedTokens} - - - - {row.outputTokens} - - - + )} + )} - - {row.bucket && row.bucket.remainingFraction != null && ( - - {(row.bucket.remainingFraction * 100).toFixed(1)}% - {row.bucket.resetTime && ( - - {' '} - ( - {(function (t) { - const formatted = formatResetTime(t); - return formatted === 'Resetting...' || - formatted === '< 1m' - ? formatted - : `Resets in ${formatted}`; - })(row.bucket.resetTime)} - ) - - )} - - )} - - ))} - + + )) + ) : ( + + No model usage recorded. + )} {cacheEfficiency > 0 && !showQuotaColumn && rows.length > 0 && ( diff --git a/packages/cli/src/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap index 85e4f86228..11445468f1 100644 --- a/packages/cli/src/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap @@ -5,6 +5,8 @@ exports[` > should display a single model correctly 1`] = ` │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -28,6 +30,8 @@ exports[` > should display conditional rows if at least one │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro gemini-2.5-flash │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -51,6 +55,8 @@ exports[` > should display role breakdown correctly 1`] = ` │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -78,6 +84,8 @@ exports[` > should display stats for multiple models correc │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro gemini-2.5-flash │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -101,6 +109,8 @@ exports[` > should filter out invalid role names 1`] = ` │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -121,6 +131,8 @@ exports[` > should handle large values without wrapping or │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -144,6 +156,8 @@ exports[` > should handle long role name layout 1`] = ` │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -170,6 +184,8 @@ exports[` > should not display conditional rows if no model │ │ │ Model Stats For Nerds │ │ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ │ │ │ Metric gemini-2.5-pro │ │ ────────────────────────────────────────────────────────────────────────────────────────────── │ @@ -188,6 +204,11 @@ exports[` > should not display conditional rows if no model exports[` > should render "no API calls" message when there are no active models 1`] = ` "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │ │ +│ Model Stats For Nerds │ +│ │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ No API calls have been made in this session. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " diff --git a/packages/cli/src/ui/components/__snapshots__/StatsDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/StatsDisplay.test.tsx.snap index 8e5f8e34ee..9fecb0c4b6 100644 --- a/packages/cli/src/ui/components/__snapshots__/StatsDisplay.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/StatsDisplay.test.tsx.snap @@ -17,6 +17,14 @@ exports[` > Code Changes Display > displays Code Changes when li │ » API Time: 0s (0.0%) │ │ » Tool Time: 100ms (100.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -37,6 +45,14 @@ exports[` > Code Changes Display > hides Code Changes when no li │ » API Time: 0s (0.0%) │ │ » Tool Time: 100ms (100.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -57,6 +73,14 @@ exports[` > Conditional Color Tests > renders success rate in gr │ » API Time: 0s (0.0%) │ │ » Tool Time: 0s (0.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -77,6 +101,14 @@ exports[` > Conditional Color Tests > renders success rate in re │ » API Time: 0s (0.0%) │ │ » Tool Time: 0s (0.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -97,6 +129,14 @@ exports[` > Conditional Color Tests > renders success rate in ye │ » API Time: 0s (0.0%) │ │ » Tool Time: 0s (0.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -118,6 +158,9 @@ exports[` > Conditional Rendering Tests > hides Efficiency secti │ » Tool Time: 0s (0.0%) │ │ │ │ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ Model Reqs Input Tokens Cache Reads Output Tokens │ │ ───────────────────────────────────────────────────────────────────────── │ │ gemini-2.5-pro 1 100 0 100 │ @@ -142,6 +185,14 @@ exports[` > Conditional Rendering Tests > hides User Agreement w │ » API Time: 0s (0.0%) │ │ » Tool Time: 123ms (100.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -163,6 +214,9 @@ exports[` > Quota Display > renders model table quota percentage │ » Tool Time: 0s (0.0%) │ │ │ │ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ Model Reqs Usage left │ │ ───────────────────────────────────────────────────────────────────── │ │ gemini-2.5-flash - 50.0% │ @@ -193,6 +247,9 @@ exports[` > Quota Display > renders pooled quota even when no mo │ /auth to upgrade or switch to API key. │ │ For a full token breakdown, run \`/stats model\`. │ │ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " @@ -246,6 +303,9 @@ exports[` > Quota Display > renders quota information for unused │ » Tool Time: 0s (0.0%) │ │ │ │ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ Model Reqs Usage left │ │ ───────────────────────────────────────────────────────────────────── │ │ gemini-2.5-flash - 50.0% (Resets in 2h) │ @@ -271,6 +331,9 @@ exports[` > Quota Display > renders quota information when quota │ » Tool Time: 0s (0.0%) │ │ │ │ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ Model Reqs Usage left │ │ ───────────────────────────────────────────────────────────────────── │ │ gemini-2.5-pro 1 75.0% (Resets in 1h 30m) │ @@ -295,6 +358,14 @@ exports[` > Title Rendering > renders the custom title when a ti │ » API Time: 0s (0.0%) │ │ » Tool Time: 0s (0.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -315,6 +386,14 @@ exports[` > Title Rendering > renders the default title when no │ » API Time: 0s (0.0%) │ │ » Tool Time: 0s (0.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `; @@ -336,6 +415,9 @@ exports[` > renders a table with two models correctly 1`] = ` │ » Tool Time: 0s (0.0%) │ │ │ │ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ Model Reqs Input Tokens Cache Reads Output Tokens │ │ ───────────────────────────────────────────────────────────────────────── │ │ gemini-2.5-pro 3 500 500 2,000 │ @@ -365,6 +447,9 @@ exports[` > renders all sections when all data is present 1`] = │ » Tool Time: 123ms (55.2%) │ │ │ │ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ │ Model Reqs Input Tokens Cache Reads Output Tokens │ │ ───────────────────────────────────────────────────────────────────────── │ │ gemini-2.5-pro 1 50 50 100 │ @@ -391,6 +476,14 @@ exports[` > renders only the Performance section in its zero sta │ » API Time: 0s (0.0%) │ │ » Tool Time: 0s (0.0%) │ │ │ +│ Model Usage │ +│ Usage limits span all sessions and reset daily. │ +│ /auth to upgrade or switch to API key. │ +│ │ +│ Model Reqs Input Tokens Cache Reads Output Tokens │ +│ ───────────────────────────────────────────────────────────────────────── │ +│ No model usage recorded. │ +│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ " `;