Skip to content

feat(agent): per-call output speed (tokens/sec) for desktop status bar and runtime footer - #44878

Closed
beresfordsalmon wants to merge 12 commits into
NousResearch:mainfrom
beresfordsalmon:feat/tps-display
Closed

beresfordsalmon wants to merge 12 commits into
NousResearch:mainfrom
beresfordsalmon:feat/tps-display

Conversation

@beresfordsalmon

Copy link
Copy Markdown

Summary

Stores per-API-call timing and token counts on the agent so downstream consumers (desktop status bar, gateway runtime footer, CLI status bar) can display real-time tokens-per-second.

  • run_agent.py: Initialize last_api_duration / last_output_tokens on agent
  • agent/conversation_loop.py: Persist per-call api_duration + output_tokens after each successful API response
  • tui_gateway/server.py: Compute output_speed in _get_usage() (read by desktop session.info events and session.usage RPC)
  • cli.py: Expose output_speed in TUI status bar when display.show_output_speed is enabled
  • gateway/runtime_footer.py: Add tps field for gateway platforms (messaging, desktop response footer)
  • gateway/run.py: Wire per-call stats into agent result + footer call site
  • hermes_cli/config.py: Add display.show_output_speed toggle (default off)

Validation

  • Verified end-to-end: conversation_loop.py SETs last_api_duration and last_output_tokens (confirmed via debug log)
  • tui_gateway/server.py _get_usage() reads them and emits output_speed (confirmed 48.0 tok/s for DeepSeek v4 Pro)
  • Desktop frontend already has output-speed status bar item (needs rebuild to display)

Closes #9527
Closes #26877

McSal added 7 commits June 12, 2026 12:50
…r and runtime footer

Store last_api_duration and last_output_tokens on the agent after each
API call so the gateway can compute output_speed for the desktop status
bar and runtime footer tps field.

- run_agent.py: initialize last_api_duration / last_output_tokens
- agent/conversation_loop.py: persist per-call timing + token counts
- tui_gateway/server.py: compute output_speed in _get_usage()
- cli.py: expose output_speed in CLI status bar (show_output_speed config)
- gateway/runtime_footer.py: add tps field for gateway platforms
- gateway/run.py: wire per-call stats into footer + agent result
- hermes_cli/config.py: add display.show_output_speed toggle (default off)

Closes #9527
- Add /tps to command registry
- Add _show_tps handler in CLI
- Add session.tps RPC in gateway server
- Pass gateway port + session_id to slash worker
- Slash worker calls session.tps when local agent has no data
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery labels Jun 12, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good feature addition. Adds per-call output speed (tokens/sec) display to desktop status bar, runtime footer, and /tps CLI command. Tracks last_api_duration and last_output_tokens per call. Clean implementation across 11 files. No issues found.

McSal added 2 commits June 12, 2026 13:42
peak_speed = (last_output_tokens + session_reasoning_tokens) / last_api_duration
This is the true decode rate including hidden reasoning tokens.

- _get_usage() now emits peak_speed in every session.info event
- CLI _get_status_bar_snapshot() also exposes peak_speed
- peak_speed is available to the desktop status bar via currentUsage?.peak_speed
  (frontend must opt-in: s/output_speed/peak_speed in use-statusbar-items.tsx)
@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 14, 2026
@fleps

fleps commented Aug 12, 2026

Copy link
Copy Markdown

I was thinking on doing a plugin for my Desktop then found this PR.
This should really be an OOTB feature.

@spfcraze

Copy link
Copy Markdown

This was generated by AI during triage.

Summary:
The peak_speed figures (in _get_usage and the /tps "Peak speed (est)" line) add the session-cumulative session_reasoning_tokens to the last call's output but divide by only that call's duration, so the rate inflates with every prior reasoning-heavy call.

Problems:
session_reasoning_tokens is a running session total (agent.session_reasoning_tokens += canonical_usage.reasoning_tokens per call, reset to 0 at session start), while last_api_duration and last_output_tokens describe only the most recent call. In tui_gateway/server.py the _get_usage peak-speed branch (around line 2150) sets total_out = last_out + reasoning, where reasoning is that session total, then divides by last_dur. After a few reasoning-heavy calls the numerator keeps growing while the denominator stays a single call, so peak_speed grows without bound and can dwarf the actual rate. The /tps handler (around line 8761) and the CLI's status-bar snapshot and /tps command repeat the same last_out + session_reasoning_tokens over last_dur arithmetic, so every client surfaces the same inflated number.

Solution:
Record the last call's own reasoning tokens (e.g. set last_reasoning_tokens = canonical_usage.reasoning_tokens alongside last_output_tokens in the same per-call block) and compute the peak from (last_output_tokens + last_reasoning_tokens) / last_api_duration. If a session-averaged figure is intended instead, pair the cumulative reasoning numerator with the session-cumulative duration, not the single call's.

Evidence

no deterministic fact backs this claim — model belief, not executed or read evidence


Checked against b8e69c7 — the tip of feat/tps-display when this was written — and fa83af3, main at the same moment.

Resolve conflicts from two months of main drift:
- gateway/runtime_footer.py: keep both turn_seconds (main) and
  response_tokens/elapsed_ms (PR) params on build_footer_line.
- gateway/run.py: re-apply last_output_tokens/last_api_duration capture
  inside the extracted TurnRunner result path (run_sync moved to
  gateway/turn_runner ctx flow on main).
- hermes_cli/config_defaults.py: DEFAULT_CONFIG moved out of config.py;
  add display.show_output_speed there.
- tui_gateway: server.py split into methods_*.py on main — re-apply
  session.tps RPC in methods_session.py and the direct /tps handler in
  methods_tools.py slash.exec; keep _get_usage output_speed/peak_speed.
  Drop dead HERMES_SLASH_GATEWAY_PORT env plumbing (superseded by the
  direct slash.exec handler within the PR's own commit history).
- cli.py: re-apply /tps dispatch + _show_tps + status-bar t/s segment on
  top of the battery-item refactor.
- apps/desktop i18n: add missing shell.statusbar.tokensPerSecond key
  (en/zh/zh-hant/ja/ar + types).
…issing i18n

- tui_gateway/server.py: numeric-coerce last_api_duration/last_output_tokens
  in _get_usage so partially-built agents and test doubles (MagicMock) can't
  crash the usage snapshot with a TypeError on comparison.
- hermes_cli/commands.py: route /tps via /hermes tps on Slack
  (_SLACK_VIA_HERMES_ONLY) — a native slot at the 50-slash cap would clamp
  /insights and break Telegram parity.
- contributors/emails: map mcsal@local -> beresfordsalmon (CI attribution gate).
@teknium1

Copy link
Copy Markdown
Collaborator

Rebased this onto current main (merge commit, contributor commits preserved) and hardened it as part of the weekly Kilo Code scout run (their #12434/#13164 ship the same tok/s display, which independently validates the feature):

  • Resolved ~2 months of drift: DEFAULT_CONFIG moved to config_defaults.py, tui_gateway/server.py split into methods_*.py (re-applied session.tps RPC in methods_session.py and the direct /tps handler in methods_tools.py), gateway run_sync extraction into TurnRunner, battery status-bar refactor in cli.py.
  • Dropped the dead HERMES_SLASH_GATEWAY_PORT env plumbing (superseded within this PR's own history by the direct slash.exec handler).
  • Hardened _get_usage with numeric coercion (MagicMock/partial agents crashed two compression tests).
  • Routed /tps via /hermes tps on Slack — a native slot at the 50-slash cap would clamp /insights.
  • Added the missing shell.statusbar.tokensPerSecond i18n key (en/zh/zh-hant/ja/ar + types).

Targeted suites green: runtime_footer + tui_gateway_server + commands + turn_summary (726 passed). Awaiting Teknium's review.

@alt-glitch alt-glitch added comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/desktop Electron desktop app (apps/desktop/*) area/config Config system, migrations, profiles area/usage-cost labels Aug 18, 2026
@alt-glitch alt-glitch added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages and removed sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 18, 2026
@abdulchotu7

Copy link
Copy Markdown

Please merge this ASAP

@beresfordsalmon beresfordsalmon closed this by deleting the head repository Aug 29, 2026
@fleps

fleps commented Aug 30, 2026

Copy link
Copy Markdown

@beresfordsalmon why was this closed? 😞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/billing Account usage, credit usage, billing (cross-cutting) area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add tps (tokens per second) field to runtime_footer [Feature]: Add display.show_output_speed to show tokens-per-second after each response

7 participants