github →

New request

#1966: [Enhancement] Add LM Studio-style token throughput + total token count display in message footer

We'll provision a sandbox, run an agent against the issue, and open a draft PR. You can pull the branch and iterate from there.

Issue
enhancementholdux
<img width="3018" height="1824" alt="Image" src="https://github.com/user-attachments/assets/acd8a7d3-e0ae-4eff-a1da-3c9e2a9baacd" /> ## Summary

Add LM Studio-style real-time token throughput (token/s) and **total token countdisplay in the assistant message footer, showingXX.X token/s · XXXX token` format.

Motivation / Use Case

When using LLM chat interfaces (especially for cost monitoring and performance tuning), users need quick visibility into:

  1. Token generation speed — how fast the model is responding
  2. Total tokens consumed per turn — for cost tracking and context window awareness

LM Studio already provides this UX pattern and users familiar with it expect similar information density. Currently Hermes WebUI shows input/output token counts but lacks:

  • Real-time generation speed (token/s)
  • Aggregated total token count in a prominent position

Proposed Behavior

Display Format

┌─────────────────────────────────────────────────────┐
│ [Assistant response content...]                     │
├─────────────────────────────────────────────────────┤
│ 15.0k in · 115 out   50.4 token/s · 15.1k token   │
│                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  │
│              Speed (TPS) + Total Tokens             │
└─────────────────────────────────────────────────────┘

Two States

StateDisplayWhen
Streaming (in progress)XX.X token/s onlymetering events fire, _turnUsage not yet available
Completed (done)XX.X token/s · XXXX tokendone event fires, _turnUsage populated

Formatting Rules

  • Speed first, token count second (LM Studio convention)
  • Separator: · (middle dot, consistent with existing in · out format)
  • Unit: full word token (not abbreviated tok)
  • Large numbers: auto-format with suffixes (1500015.0k, 25000002.5M)
  • Position: inside .msg-foot, after .msg-usage-inline or .msg-time element
  • Style: muted color, small font (12px), semi-transparent (opacity 0.85), bold weight

Technical Approach

Data Sources (Already Available)

DataSource EventField
input_tokensdone_turnUsage.input_tokens
output_tokensdone_turnUsage.output_tokens
total_tokenscomputedinput_tokens + output_tokens
tokens/secmeteringcalculated from token delta / time delta

Implementation Scope

Single file modification: static/messages.js

  1. New helper function _fmtTokens(n) — ~7 lines

    • Formats large numbers with k/M suffixes
  2. Enhanced function _injectLiveTpsToMessage(tps) — ~15 lines modification

    • Reads last assistant message's _turnUsage from S.messages global array
    • Computes total_tokens = input + output
    • Conditionally renders speed-only or speed+tokens format
    • Creates/updates .msg-speed-inline DOM element

Total change: ~22 lines of code across 2 functions in 1 file.

No Backend Changes Required

All data is already emitted by the backend via WebSocket events (metering, done). This is purely a frontend enhancement.

Reference Implementation

A working proof-of-concept is available at:
🔗 messages.js (fork with enhancement)

Note: This is a fork for demonstration only. The relevant changes are in _fmtTokens() and _injectLiveTpsToMessage() functions (~22 lines total, approximately lines 1199-1240).

Tested on Hermes Agent v0.13.0 (2026.5.7) with the following verified results:

  • ✅ Streaming phase shows speed only
  • ✅ Completion phase shows XX.X token/s · XXXX token
  • ✅ Multi-turn conversations display independently per message
  • ✅ No duplicate elements, no layout breakage
  • ✅ Works with existing in · out usage display
  • ✅ Zero backend changes needed

Key Code Snippet (Reference)

// Helper: format large numbers
function _fmtTokens(n) {
  if (!n || n < 0) return '0';
  if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
  if (n >= 1e3) return (n / 1e3).toFixed(1) + 'k';
  return String(n);
}

// Enhanced injection (core logic)
function _injectLiveTpsToMessage(tps) {
  const activeFoot = document.querySelector('.assistant-turn:last-child .msg-foot');
  if (!activeFoot) return;

  // Get usage from global S.messages array (reliable data source)
  const lastAsstMsg = S.messages && Array.isArray(S.messages)
    ? [...S.messages].reverse().find(m => m.role === 'assistant') : null;
  const usage = lastAsstMsg?._turnUsage || null;
  const totalTok = (usage?.input_tokens || 0) + (usage?.output_tokens || 0);

  const tpsVal = typeof tps === 'number' ? tps.toFixed(1) : '—';
  // ... DOM element creation/update ...
  
  // Render: speed + token (LM Studio style)
  speedEl.textContent = totalTok > 0
    ? `${tpsVal} token/s · ${_fmtTokens(totalTok)} token`
    : `${tpsVal} token/s`;
}

Alternatives Considered

OptionProsConsVerdict
A: Current proposal (DOM injection in messages.js)Minimal change, zero backend impactNot persisted on page refreshSelected
B: Modify ui.js renderMessages()Persisted on refreshHigher risk of breaking changes, larger diff❌ Rejected
C: Backend-side footer enhancementAlways availableRequires server code change, more complex❌ Overkill
D: Browser extensionNo source change neededMaintenance burden, distribution problem❌ Impractical

Known Limitations

  1. Non-persistent on refresh: Speed/token info disappears on page reload (same behavior as current TPS display). Fixing this would require modifying ui.js render logic.
  2. Requires metering events: If streaming doesn't emit metering events, speed won't show (token count from done event still works).

Screenshots / Mockup

During streaming:

15.0k in · 115 out   50.4 token/s    10:42 AM
                        ↑
                   Speed only (usage not yet available)

After completion:

15.0k in · 115 out   50.4 token/s · 15.1k token    10:43 AM
                       ^^^^^^^^^^^^^^^^^^^^^^^^
                  Speed + Total Token Count

Checklist for Implementation

  • Add _fmtTokens() helper function to messages.js
  • Enhance _injectLiveTpsToMessage() with token count logic
  • Test streaming state (speed-only display)
  • Test completion state (speed + token display)
  • Test multi-turn conversation isolation
  • Verify no regression in existing usage display
  • Test on Chrome, Firefox, Edge

Labels Suggested

enhancement ui good first issue webui


Note: This is a ready-to-implement feature request with a working reference implementation. The approach requires modifying only messages.js (~22 lines) with no backend changes.


---

Assessmentadvisory
feature●● medium85% confidence

Displaying real-time token throughput in the message footer requires frontend streaming UI changes and possibly backend metering event integration.

Likely files
  • static/js/chat.js
  • static/js/message_footer.js
  • static/css/app.css
  • api/streaming.py
  • api/usage.py
Create the request

This opens a fresh agent run and a draft PR for issue #1966.

Cancel