Skip to content

feat(ui): Display token usage in the loading/progress indicator #2445

Merged
qqqys merged 5 commits intoQwenLM:mainfrom
qqqys:feat/token_display
Mar 18, 2026
Merged

feat(ui): Display token usage in the loading/progress indicator #2445
qqqys merged 5 commits intoQwenLM:mainfrom
qqqys:feat/token_display

Conversation

@qqqys
Copy link
Copy Markdown
Collaborator

@qqqys qqqys commented Mar 17, 2026

TLDR

This PR enhances the LoadingIndicator component to display real-time output token counts during AI responses, providing users with visibility into token usage. Also improves the status line formatting for better readability.

token

Dive Deeper

Token Display Feature:

  • Added candidatesTokens prop to LoadingIndicator component
  • Tokens are aggregated from sessionStats.metrics.models in the Composer component
  • New formatTokenCount utility function formats tokens intelligently:
    • Exact count for < 1,000 tokens (e.g., "847")
    • One decimal for 1,000-9,999 (e.g., "5.4k")
    • No decimal for 10,000+ (e.g., "100k")
  • Token display is hidden in narrow terminals (< 80 columns) to preserve layout

Formatting Improvements:

  • Status line reformatted from (esc to cancel, 5s) to (5s · ↓ 847 tokens · esc to cancel)
  • Uses arrow notation (↓) to indicate output tokens being generated
  • Clean separator pattern using · for better visual hierarchy

Reviewer Test Plan

  1. Run npm start to launch the CLI
  2. Send any prompt to the AI
  3. Observe the loading indicator during response generation - should show token count incrementing
  4. Test with narrow terminal (< 80 cols) to verify tokens are hidden
  5. Run tests: npm run test -- LoadingIndicator

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

No linked issues

Resolves #2013

…e formatting

- Added candidatesTokens prop to LoadingIndicator for displaying token counts.
- Updated formatting to show elapsed time and token counts inline.
- Refactored tests to validate new token display functionality and formatting changes.
- Introduced formatTokenCount utility for consistent token count representation.

This improves user feedback during loading states by providing clearer information on token usage.
@github-actions
Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR enhances the LoadingIndicator component to display real-time output token counts during AI responses, along with improved status line formatting. The implementation is well-structured with comprehensive test coverage, but there are several issues that should be addressed before merging.

🔍 General Feedback

  • The feature addresses a genuine user need for visibility into token usage during generation
  • Good separation of concerns with the token formatting logic extracted to a utility function
  • Test coverage is comprehensive, covering edge cases and responsive layout behavior
  • The arrow notation (↓) for output tokens is intuitive and follows common UI patterns
  • The conditional hiding of tokens in narrow terminals shows good attention to UX

🎯 Specific Feedback

🔴 Critical

  • File: packages/cli/src/ui/components/LoadingIndicator.tsx:48-56 - The candidatesTokens prop is added to the interface but the promptTokens prop referenced in tests doesn't exist in the component. This inconsistency suggests the test was written for a different API than what was implemented. The tests use promptTokens and candidatesTokens props, but the component only accepts candidatesTokens.

  • File: packages/cli/src/ui/components/LoadingIndicator.test.tsx:320-329 - Tests reference promptTokens prop which doesn't exist in the component interface. This will cause test failures:

    <LoadingIndicator
      {...defaultProps}
      promptTokens={1500}  // This prop doesn't exist in interface
      candidatesTokens={847}
    />

🟡 High

  • File: packages/cli/src/ui/components/Composer.tsx:31-38 - The token aggregation logic uses sessionStats.metrics.models without checking if the models object is empty or if the metrics structure exists. This could lead to undefined behavior or errors if the metrics structure changes:

    const tokens = Object.values(sessionStats.metrics.models).reduce(...)

    Consider adding defensive checks or default values.

  • File: packages/cli/src/ui/utils/formatters.ts:58-66 - The formatTokenCount function doesn't handle negative numbers. While token counts shouldn't be negative, defensive programming suggests adding a guard:

    export const formatTokenCount = (count: number): string => {
      if (count < 0) return '0'; // Add this check
      if (count < 1000) {
        // ...

🟢 Medium

  • File: packages/cli/src/ui/components/LoadingIndicator.tsx:48-56 - The token string formatting uses a hardcoded arrow symbol (↓). Consider extracting this to a constant or using a more semantic representation for consistency and easier theming:

    const OUTPUT_TOKEN_ARROW = '↓'; // Extract to constants
  • File: packages/cli/src/ui/components/LoadingIndicator.tsx:48-56 - The format string t('({{time}}{{tokens}} · esc to cancel)', {...}) creates a translation string dynamically. This may cause issues with i18n systems that extract translation keys statically. Consider using a fixed key with parameters.

  • File: packages/cli/src/ui/utils/formatters.ts:58-66 - The formatting thresholds (1000, 10000) are magic numbers. Consider extracting to named constants for clarity:

    const TOKEN_FORMAT_THRESHOLD_EXACT = 1000;
    const TOKEN_FORMAT_THRESHOLD_DECIMAL = 10000;

🔵 Low

  • File: packages/cli/src/ui/components/LoadingIndicator.tsx:24 - The new candidatesTokens prop should have a JSDoc comment explaining its purpose and expected range of values.

  • File: packages/cli/src/ui/utils/formatters.test.ts:159-178 - Consider adding a test case for negative numbers (if you add the guard) and for very large numbers (e.g., 1000000+) to ensure the formatting remains readable.

  • File: packages/cli/src/ui/components/LoadingIndicator.test.tsx:360-369 - The test "should format tokens inline with time and cancel" uses a magic number 120 for terminal width. Consider using a named constant like WIDE_TERMINAL_WIDTH for clarity.

✅ Highlights

  • Excellent test coverage: The new token display feature has 6 comprehensive test cases covering various scenarios including edge cases (0 tokens, undefined props), responsive behavior (narrow vs wide terminals), and formatting verification.

  • Clean utility function: The formatTokenCount function is well-designed with clear formatting rules that balance precision and readability (exact for < 1k, one decimal for 1k-10k, no decimal for 10k+).

  • Thoughtful UX decisions: Hiding tokens in narrow terminals (< 80 columns) prevents layout breakage and shows consideration for different user environments.

  • Consistent formatting pattern: The new status line format (5s · ↓ 847 tokens · esc to cancel) uses consistent separators (·) and creates a clean visual hierarchy.

  • Good use of existing patterns: The implementation leverages existing utilities (isNarrowWidth, formatDuration) and follows established component patterns in the codebase.

Copy link
Copy Markdown
Collaborator

@tanzhenxin tanzhenxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@qqqys qqqys merged commit 28148d3 into QwenLM:main Mar 18, 2026
2 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display token usage in the loading/progress indicator

2 participants