Skip to content

fix(gateway): /status command always shows 0 tokens#11088

Closed
rustyorb wants to merge 1 commit into
NousResearch:mainfrom
rustyorb:fix/status-token-count
Closed

fix(gateway): /status command always shows 0 tokens#11088
rustyorb wants to merge 1 commit into
NousResearch:mainfrom
rustyorb:fix/status-token-count

Conversation

@rustyorb
Copy link
Copy Markdown

Summary

Fixes #11087

SessionEntry.total_tokens was initialized to 0 but never written to. The agent tracks cumulative tokens on agent.session_total_tokens, but nobody copies that back to the session store.

Changes

  • gateway/session.py: update_session() accepts optional total_tokens parameter
  • gateway/run.py: Pipes agent.session_total_tokens through the result dict into update_session() after each agent run (both success and error return paths in run_sync())
  • tests/gateway/test_status_command.py: Updated assertion for new call signature

Test Plan

pytest tests/gateway/test_status_command.py -v  # 4 passed

Verified manually on Discord — /status now shows cumulative session tokens after gateway restart.

SessionEntry.total_tokens was initialized to 0 in the constructor but
never updated during the session lifecycle. The agent tracks cumulative
tokens on agent.session_total_tokens, but that value was never written
back to the session store.

Changes:
- session.py: update_session() accepts optional total_tokens parameter
- run.py: pipe agent.session_total_tokens through result dict into
  update_session() after each agent run (both success and error paths)
- test_status_command.py: updated assertion for new call signature
Copilot AI review requested due to automatic review settings April 16, 2026 15:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes the gateway /status command reporting Tokens: 0 by persisting the agent’s cumulative session token count into SessionEntry.total_tokens.

Changes:

  • Extend SessionStore.update_session() to optionally persist total_tokens.
  • Pipe agent.session_total_tokens into the gateway’s session-store update path after agent runs.
  • Update /status-related test to match the new update_session() call signature.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
gateway/session.py Adds optional total_tokens persistence to session metadata updates.
gateway/run.py Passes session total token count through agent results into update_session().
tests/gateway/test_status_command.py Updates assertion to include the new total_tokens kwarg.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gateway/run.py
self.session_store.update_session(
session_entry.session_key,
last_prompt_tokens=agent_result.get("last_prompt_tokens", 0),
total_tokens=agent_result.get("session_total_tokens", 0),
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

agent_result.get("session_total_tokens", 0) will write 0 into the session store whenever the key is missing (e.g., proxy-mode results and early error returns that don't include session_total_tokens). That can clobber a previously accumulated SessionEntry.total_tokens and reintroduce the “Tokens: 0” issue after any non-token-reporting run. Prefer passing None when the key is absent (or conditionally include the kwarg only when present) so update_session() leaves the stored value unchanged.

Suggested change
total_tokens=agent_result.get("session_total_tokens", 0),
total_tokens=agent_result.get("session_total_tokens"),

Copilot uses AI. Check for mistakes.
Comment on lines 150 to 154
runner.session_store.update_session.assert_called_once_with(
session_entry.session_key,
last_prompt_tokens=80,
total_tokens=0,
)
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

This test currently expects total_tokens=0 even though the mocked _run_agent() result does not include session_total_tokens. That assertion will still pass if the implementation mistakenly defaults missing totals to 0 (which can overwrite real stored totals). Update the mock result to include a non-zero session_total_tokens and assert it’s passed through (or, if missing is valid, assert update_session() is called without total_tokens).

Copilot uses AI. Check for mistakes.
Comment thread gateway/run.py
Comment on lines 4065 to +4071
# Token counts and model are now persisted by the agent directly.
# Keep only last_prompt_tokens here for context-window tracking and
# compression decisions.
self.session_store.update_session(
session_entry.session_key,
last_prompt_tokens=agent_result.get("last_prompt_tokens", 0),
total_tokens=agent_result.get("session_total_tokens", 0),
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The comment says “Keep only last_prompt_tokens here…”, but this block now also persists total_tokens. Update the comment to reflect the new behavior so future readers don’t assume total_tokens is intentionally omitted.

Copilot uses AI. Check for mistakes.
@zeapsu
Copy link
Copy Markdown

zeapsu commented Apr 23, 2026

Code Review — PR #11088

Verdict: Changes Requested (1 critical bug)

🔴 Critical: Key name mismatch — success path always returns 0

**** — The success path reads the wrong key from the agent result dict:

total_tokens=agent_result.get("session_total_tokens", 0),  # ← WRONG KEY

But agent.run_conversation() returns the dict with key "total_tokens" (run_agent.py:10721):

"total_tokens": self.session_total_tokens,

So agent_result.get("session_total_tokens", 0) always returns 0 in the success path. The test passes because its mock doesn't include either key, so both resolve to the default 0.

Fix: Change the key to "total_tokens":

total_tokens=agent_result.get("total_tokens", 0),

The error/fallback paths at lines 8723, 8739, 8829 are correct — they read directly from the agent object via getattr(_agent, "session_total_tokens", 0).

⚠️ Suggestion: Also propagate input_tokens and output_tokens

The /status command only shows total_tokens currently, but SessionEntry has input_tokens and output_tokens fields too. If /status is ever enriched with a breakdown (like /usage does), those fields would also show 0. Consider also passing:

self.session_store.update_session(
    session_entry.session_key,
    last_prompt_tokens=agent_result.get("last_prompt_tokens", 0),
    total_tokens=agent_result.get("total_tokens", 0),
    input_tokens=agent_result.get("input_tokens", 0),
    output_tokens=agent_result.get("output_tokens", 0),
)

This would require extending update_session() to accept those params too, but it's a small change that future-proofs the data.

✅ Looks Good

  • The update_session() signature extension in session.py is clean
  • Both error and success return paths in run_sync() are covered (lines 8739, 8829)
  • Test updated to match new signature

Comment thread gateway/run.py
self.session_store.update_session(
session_entry.session_key,
last_prompt_tokens=agent_result.get("last_prompt_tokens", 0),
total_tokens=agent_result.get("session_total_tokens", 0),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical: wrong key name. agent.run_conversation() returns total_tokens (run_agent.py:10721), not session_total_tokens. This will always resolve to 0. Change to agent_result.get("total_tokens", 0).

Copy link
Copy Markdown

@zeapsu zeapsu left a comment

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Changes Requested (1 critical bug)

🔴 Critical

  • gateway/run.py:4071 — Key name mismatch: reads session_total_tokens from agent_result, but run_conversation() returns total_tokens. This means the success path always resolves to 0.

💡 Suggestion

  • Consider also propagating input_tokens and output_tokens to the session store for future /status enrichment.

✅ Looks Good

  • Clean update_session() signature extension in session.py
  • Both error and success return paths in run_sync() are covered
  • Test updated to match new signature

@zeapsu
Copy link
Copy Markdown

zeapsu commented Apr 23, 2026

Correction to my review — I was wrong about the key name mismatch. 🙏

I was comparing against which returns , but in comes from , which wraps the result through . The inner function constructs its own return dict with the key (line 8829). So is correct.

The PR LGTM — withdrawing my request for changes.

Copy link
Copy Markdown

@zeapsu zeapsu left a comment

Choose a reason for hiding this comment

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

Correction: I was wrong about the key name mismatch. agent_result comes from _run_agent(), not run_conversation(). The run_sync() inner function constructs its own dict with key session_total_tokens (line 8829). So agent_result.get("session_total_tokens", 0) is correct. LGTM!

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery labels Apr 23, 2026
@alt-glitch
Copy link
Copy Markdown
Collaborator

Related to #13820 (alternative fix reading from SQLite) and #5960 — all address /status showing 0 tokens.

@teknium1
Copy link
Copy Markdown
Contributor

teknium1 commented May 1, 2026

Fixed on main in 7abc9ce via #17158 using the alternate approach — rather than mirror totals into SessionStore, /status now reads directly from SessionDB (SQLite), which run_agent.py already writes to per API call. Closing this as superseded. Thanks for the fix.

@teknium1 teknium1 closed this May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gateway /status command always reports 0 tokens

5 participants