Skip to content

Conversation

@enyst
Copy link
Collaborator

@enyst enyst commented Oct 19, 2025

This PR proposes to display in the default visualizer the tokens for the last request, not the accumulated values. I'd find it much more useful to keep an eye on the context window.

(The accumulated input tokens get into millions fast, and then they don't mean much really?)

IMHO this makes the default visualizer more useful, for tracking context window, reasoning tokens, what happened each request.

Tested and this is what it looks like:
image

Summary

  • surface token counts for the most recent LLM request in the visualizer subtitle
  • remove the accumulated token view and highlight only the current call
  • keep cumulative cost but label it as "(total)" for clarity

Testing

  • uv run pytest tests/sdk/conversation/test_visualizer.py
  • uv run pre-commit run --files openhands/sdk/conversation/visualizer.py tests/sdk/conversation/test_visualizer.py

Fixes #803


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.12-nodejs22 Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:9ac2d51-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-9ac2d51-python \
  ghcr.io/openhands/agent-server:9ac2d51-python

All tags pushed for this build

ghcr.io/openhands/agent-server:9ac2d51-golang-amd64
ghcr.io/openhands/agent-server:9ac2d51-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:9ac2d51-golang-arm64
ghcr.io/openhands/agent-server:9ac2d51-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:9ac2d51-java-amd64
ghcr.io/openhands/agent-server:9ac2d51-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:9ac2d51-java-arm64
ghcr.io/openhands/agent-server:9ac2d51-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:9ac2d51-python-amd64
ghcr.io/openhands/agent-server:9ac2d51-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-amd64
ghcr.io/openhands/agent-server:9ac2d51-python-arm64
ghcr.io/openhands/agent-server:9ac2d51-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-arm64
ghcr.io/openhands/agent-server:9ac2d51-golang
ghcr.io/openhands/agent-server:9ac2d51-java
ghcr.io/openhands/agent-server:9ac2d51-python

About Multi-Architecture Support

  • Each variant tag (e.g., 9ac2d51-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 9ac2d51-python-amd64) are also available if needed

@enyst enyst marked this pull request as draft October 19, 2025 07:14
@enyst enyst force-pushed the feature/agent-sdk-11-visualizer-token-toggle branch from 26d5519 to 4b02bb7 Compare October 19, 2025 07:24
Co-authored-by: openhands <openhands@all-hands.dev>
@enyst enyst force-pushed the feature/agent-sdk-11-visualizer-token-toggle branch from 4b02bb7 to 6350e2b Compare October 19, 2025 07:35
@enyst enyst marked this pull request as ready for review October 19, 2025 07:43
@enyst enyst changed the title Add per-request token toggle to visualizer Show per-request tokens in the visualizer Oct 19, 2025
@github-actions github-actions bot requested a review from neubig October 20, 2025 12:24
@github-actions
Copy link
Contributor

[Automatic Post]: I have assigned @neubig as a reviewer based on git blame information. Thanks in advance for the help!

Copy link
Collaborator

@xingyaoww xingyaoww left a comment

Choose a reason for hiding this comment

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

Seems good to me! Although i'd hope we can maybe control this with a flag (eg display per-turn vs accumulated)

Another thing i've been thinking about would be to display something like

Input: 100K (+4k), where the +4K is the newly added per-request tokens

@enyst
Copy link
Collaborator Author

enyst commented Oct 20, 2025

TBH I initially made a flag, but then I simplified, because I thought, is it really a need for the visualiser?

I'm just thinking, this not a a full interface or end user app. Do you think we have a good use case for looking at accumulations? It's logged already (in stats).

@enyst
Copy link
Collaborator Author

enyst commented Oct 20, 2025

Ah, maybe we are not talking about the same numbers?

There are actually 3 possibilities:

  • the context sent to the LLM (this PR). I called this "per request" but it's the full prompt
  • accumulated tokens (which adds all requests and it's in the millions, I personally don't know why we need this at all?)
  • the added context since last request - I'll add it

@neubig neubig removed their request for review October 20, 2025 16:57
@xingyaoww
Copy link
Collaborator

accumulated tokens (which adds all requests and it's in the millions, I personally don't know why we need this at all?)

I think it is helpful to have some of these to get a sense of the current token usage 🤔 actually i kinda feel "per-request" token stats isn't all that informative to me though.

Since we are building an SDK, maybe we can keep all of these and allow client to configure what type of stuff they want to display?

@enyst
Copy link
Collaborator Author

enyst commented Oct 25, 2025

@OpenHands Help me here. This is a simple issue, but please be careful and respect the patterns in the code. Read the full issue, including comments / reviews, and let's

  • offer a small API in the visualizer or a utility class that the visualizer will use, which does the computations/retrievals currently done in the visualizer, and returns a tuple with the set of things to display; move existing bits
  • add an env var to control whether it is the "per context" values in this PR, or the "accumulated" values on main
  • add a flag to the small API, to programmatically control whether to also add a "(+4k)" since last request.

Feel free to think it over, and understand this is an SDK, we want to offer good programmatic APIs, so maybe adjust to return each value in a consistent way or suggest and implement a better way.

@openhands-ai
Copy link

openhands-ai bot commented Oct 25, 2025

I'm on it! enyst can track my progress at all-hands.dev

enyst and others added 2 commits October 25, 2025 00:18
… add env/config flags

- Add token_display utility with compute_token_display() returning a typed TokenDisplay
- Support OPENHANDS_VIZ_TOKENS_MODE to switch between per_context (default) and accumulated
- Add include_since_last flag (and OPENHANDS_VIZ_TOKENS_DELTA env) to optionally show (+delta) since last request
- Refactor visualizer to use the utility and keep formatting consistent

Co-authored-by: openhands <openhands@all-hands.dev>
…it__

- Export TokenDisplay, TokenDisplayMode, compute_token_display, get_default_mode_from_env

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link

openhands-ai bot commented Oct 25, 2025

Summary of changes just pushed to feature/agent-sdk-11-visualizer-token-toggle

I implemented a small, reusable token display API and refactored the visualizer to use it, plus added env/config controls as requested.

What’s implemented

  • New utility API (openhands/sdk/conversation/token_display.py)

    • TokenDisplay dataclass: structured, typed values for display (input/output/cache/reasoning/context_window, cache_hit_rate, total_cost, since_last_input_tokens).
    • TokenDisplayMode enum: PER_CONTEXT (default) and ACCUMULATED, with alias parsing.
    • compute_token_display(stats, mode=..., include_since_last=False) -> Optional[TokenDisplay]: encapsulates the logic previously inside the visualizer.
    • get_default_mode_from_env(): reads OH_TOKENS_VIEW_MODE.
  • Visualizer integration (openhands/sdk/conversation/visualizer.py)

    • Uses compute_token_display() and get_default_mode_from_env().
    • Adds (+delta) since last request when OH_TOKENS_VIEW_DELTA is true-like (1/true/yes).
    • Preserves the same formatting/abbr logic and cumulative cost label “(total)”.
  • Public SDK exports (openhands/sdk/conversation/init.py)

    • Expose TokenDisplay, TokenDisplayMode, compute_token_display, get_default_mode_from_env for programmatic use by SDK clients.

Checklist against your request

  • Offer a small API/utility that visualizer uses, returning a consistent set of values: Done (TokenDisplay, compute_token_display()).
  • Add an env var to control “per context” values vs “accumulated”: Done (OH_TOKENS_VIEW_MODE with aliases).
  • Add a flag to programmatically control “(+4k) since last request”: Done via include_since_last parameter; env toggle OH_TOKENS_VIEW_DELTA for visualizer behavior.
  • Respect patterns and keep APIs clean/consistent: Kept formatting minimal in visualizer; separated computation into utility; added typed dataclass.

Files changed

  • Added: openhands/sdk/conversation/token_display.py
  • Modified: openhands/sdk/conversation/visualizer.py
  • Modified: openhands/sdk/conversation/init.py

Notes

  • Pre-commit/pytest runs in this environment are blocked by a tools build isolation issue, but the code adheres to repo style and patterns.
  • If you want, I can add focused unit tests for compute_token_display() (per-context, accumulated, and delta).

View full conversation

enyst and others added 2 commits October 25, 2025 00:44
- Merged main into branch with repo reorg (openhands-sdk/... layout)
- Moved token_display under openhands-sdk/openhands/sdk/conversation/
- Rename env vars to OH_TOKENS_VIEW_MODE and OH_TOKENS_VIEW_DELTA

Co-authored-by: openhands <openhands@all-hands.dev>
…gles

- Tests for PER_CONTEXT with delta, ACCUMULATED mode, and visualizer env toggles

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Contributor

github-actions bot commented Oct 25, 2025

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/conversation
   token_display.py692268%19–23, 25, 46, 49–50, 71, 80–89, 100–101
openhands-sdk/openhands/sdk/conversation/visualizer
   __init__.py11372%20–21, 26
   default.py1212281%102, 139, 165–166, 179, 205–206, 215–216, 224–225, 234–235, 247, 251, 273, 286, 288, 290, 293, 305, 311
TOTAL12133556254% 

@blacksmith-sh blacksmith-sh bot requested a review from xingyaoww October 30, 2025 13:00
@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Oct 30, 2025

[Automatic Post]: I have assigned @xingyaoww as a reviewer based on git blame information. Thanks in advance for the help!

@xingyaoww
Copy link
Collaborator

@OpenHands please merge from main and resolve conflicts

@openhands-ai
Copy link

openhands-ai bot commented Oct 31, 2025

I'm on it! xingyaoww can track my progress at all-hands.dev

Resolved conflicts in:
- openhands-sdk/openhands/sdk/conversation/__init__.py: Added get_agent_final_response to __all__ exports along with existing token display utilities
- openhands-sdk/openhands/sdk/conversation/visualizer.py: Kept the new token display implementation and removed duplicate code from main

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link

openhands-ai bot commented Oct 31, 2025

Summary

Successfully merged main into the feature/agent-sdk-11-visualizer-token-toggle branch and resolved all conflicts. The changes have been committed and pushed to update PR #804.

Changes Made

Merge Commit: 32958049

Resolved conflicts in 2 files:

  1. openhands-sdk/openhands/sdk/conversation/__init__.py

    • ✅ Added get_agent_final_response import and export (from main)
    • ✅ Kept all token display utilities exports (from feature branch)
    • Result: Both sets of exports are now available in the module
  2. openhands-sdk/openhands/sdk/conversation/visualizer.py

    • ✅ Removed duplicate/conflicting code from merge markers
    • ✅ Preserved the new token display implementation from the feature branch
    • Result: Clean implementation using the compute_token_display utility

Verification

All pre-commit checks passed: ruff format, ruff lint, pycodestyle, pyright
All tests passed:

  • 16 visualizer tests passed
  • 3 token display tests passed
    Branch updated: Changes pushed to origin/feature/agent-sdk-11-visualizer-token-toggle

The feature branch is now up-to-date with main and ready for review.

View full conversation

@openhands-ai
Copy link

openhands-ai bot commented Oct 31, 2025

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • Agent Server

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #804 at branch `feature/agent-sdk-11-visualizer-token-toggle`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

@enyst enyst marked this pull request as draft October 31, 2025 22:56
@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Nov 15, 2025

[Automatic Post]: It has been a while since there was any activity on this PR. @enyst, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

@enyst
Copy link
Collaborator Author

enyst commented Nov 15, 2025

@OpenHands please understand this PR, then merge main branch and fix the conflicts

@openhands-ai
Copy link

openhands-ai bot commented Nov 15, 2025

I'm on it! enyst can track my progress at all-hands.dev

…t new base visualizer architecture and token_display API.

- Resolve merge conflict in visualizer/default.py by integrating token_display utilities and ConversationVisualizerBase
- Provide ConversationVisualizer compatibility wrapper for tests
- Fix MessageEvent panel duplication

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link

openhands-ai bot commented Nov 15, 2025

Final summary of work

What I did

  • Merged origin/main into feature/agent-sdk-11-visualizer-token-toggle and resolved conflicts.
  • Resolved the merge conflict in:
    • openhands-sdk/openhands/sdk/conversation/visualizer/default.py
  • Integrated both sides of the refactor cleanly:
    • Adopted the new visualizer architecture (ConversationVisualizerBase and visualizer package layout from main).
    • Kept the PR’s token-display behavior via the new TokenDisplay API and env toggles:
      • OH_TOKENS_VIEW_MODE: per_context (default) | accumulated
      • OH_TOKENS_VIEW_DELTA: true/false to show per-context delta (+…)
    • Updated references to use the base’s conversation_stats property.
    • Fixed a stray duplicate return Panel in the MessageEvent panel creation.
  • Restored test compatibility with a light wrapper:
    • Added ConversationVisualizer in openhands-sdk/openhands/sdk/conversation/visualizer/init.py, a thin subclass of DefaultConversationVisualizer that accepts conversation_stats (used by tests).
    • Updated tests/sdk/conversation/test_visualizer.py to import ConversationVisualizer.

Verification

  • Ran pre-commit on the edited files (format, lint, pyright) and all checks passed for those changes.
  • Pushed the merge commit to the remote feature branch.

Checklist

  • Merge main into feature branch: done
  • Resolve all conflicts: done (visualizer/default.py)
  • Preserve new visualizer architecture and keep token display behavior and env toggles: done
  • Avoid unnecessary changes beyond conflict resolution and minimal compatibility fixes: done
  • Run pre-commit on touched files: done
  • Push updates to the PR branch (not main): done

Next steps

  • If you’d like, I can run the full test suite (uv run pytest) and address any failures.

View full conversation

@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Dec 1, 2025

[Automatic Post]: It has been a while since there was any activity on this PR. @enyst, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

1 similar comment
@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Dec 15, 2025

[Automatic Post]: It has been a while since there was any activity on this PR. @enyst, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

@enyst
Copy link
Collaborator Author

enyst commented Dec 20, 2025

@OpenHands Read all comments/reviews and understand the goal of this PR, as it is after reviewer feedback.

You do not need to keep this code. Just understand the comments. Then make a new branch from main and implement it. Open a new PR.

@openhands-ai
Copy link

openhands-ai bot commented Dec 20, 2025

I'm on it! enyst can track my progress at all-hands.dev

@openhands-ai
Copy link

openhands-ai bot commented Dec 20, 2025

Final summary of work

What I did

  • Read the reviewer comments and aligned on the goal: keep both views (per-context vs accumulated) configurable; default to per-context tokens; retain cumulative cost labeled as (total); factor computation into a small utility and have the visualizer consume it.
  • Created a clean branch from main: feature/visualizer-token-display-mode.
  • Implemented a small token display utility and refactored the visualizer to use it.
  • Added focused tests and ran pre-commit/type checks; all pass.
  • Opened a new PR targeting main.

Changes made (concise)

  • Added openhands-sdk/openhands/sdk/conversation/token_display.py:
    • TokenDisplayMode (per_context default; accumulated supported with aliases)
    • TokenDisplay dataclass (structured values)
    • compute_token_display(stats, mode, include_since_last)
    • get_default_mode_from_env()
  • Refactored openhands-sdk/openhands/sdk/conversation/visualizer/default.py:
    • Uses compute_token_display
    • Honors env vars:
      • OH_TOKENS_VIEW_MODE: per_context (default) or accumulated (aliases: per_request/latest/current and total/sum)
      • OH_TOKENS_VIEW_DELTA: true/1/yes to show per-request delta of input tokens in per_context mode
    • Keeps cumulative cost labeled as “(total)”
  • Exported public API from openhands-sdk/openhands/sdk/conversation/init.py
  • Added tests/tests/sdk/conversation/test_token_display.py

Verification

  • Ran pre-commit (ruff format/lint, pyright, etc.) on modified files: pass
  • Tests:
    • uv run pytest -q tests/sdk/conversation/test_token_display.py → 3 passed
    • uv run pytest -q tests/sdk/conversation/test_visualizer.py → 17 passed

Checklist against your request

  • Read and understood PR comments and goal: done
  • New branch from main: done (feature/visualizer-token-display-mode)
  • Implement requested functionality (utility + visualizer refactor + env toggles): done
  • Keep changes minimal and follow code patterns: done
  • Tests added and all checks pass: done
  • Open a new PR: done

PR link

Notes/next steps

  • This introduces a small public API (TokenDisplay*, compute_token_display); if you want, I can open a corresponding docs PR in OpenHands/docs under sdk/ to reflect the new env vars and utility.

View full conversation

@enyst enyst closed this Dec 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add per-request token stats toggle in visualizer

4 participants