Skip to content

v0.2.0 - token-efficient screenshots

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 08:11
· 6 commits to master since this release

Screenshots dominate the cost of driving a desktop, and not just once: every image stays in the conversation and is re-sent as history on every later turn. Claude bills vision in 28x28 patches (tokens = ceil(w/28) * ceil(h/28)), so this release attacks that directly.

Batch your steps

Pass steps - a list of action dicts - instead of one call per action, and the whole run shares one screenshot at the end:

{"steps": [{"action": "left_click", "coordinate": [420, 300]},
           {"action": "type",  "text": "hello@example.com"},
           {"action": "key",   "text": "Tab"},
           {"action": "type",  "text": "secret"},
           {"action": "key",   "text": "Return"}]}

That is 1,125 visual tokens instead of 5,625, and one round trip instead of five - the larger saving, since each avoided turn also avoids re-sending the entire transcript. A failing step stops the run, reports which step failed, and still returns the screen. Add "screenshot": false to skip the trailing image too.

Patch-aligned downscaling

A dimension that isn't a multiple of 28 pays for a partial patch row/column carrying almost no pixels. The default MAX_DIM moves 1280 -> 1260, so a 1920x1080 primary sends 1260x700 = exactly 45x25 patches = 1,125 tokens, versus 1,196 for 1280x720. 6% off for 1.5% fewer pixels. Each axis rounds independently, so to_real() now maps each with its own scale factor and stays exact.

Unchanged-screen suppression

If under COMPUTER_USE_CHANGE_THRESHOLD of pixels moved since the last image sent, the reply is a line of text instead of a screenshot. A real desktop never produces two byte-identical frames (clock, caret, hover states all jitter ~0.5-1% of pixels), so this is a threshold, not an equality check. After COMPUTER_USE_MAX_SKIPS suppressions in a row it force-sends one, so the model can't fly blind if it lost the earlier image to context compaction.

Also

  • Tool description trimmed from ~833 to ~705 tokens (charged on every request).
  • Documentation fix: .env.example previously claimed COMPUTER_USE_IMAGE_FORMAT=jpeg "cuts tokens further". It does not - cost is by pixel dimensions, not bytes. JPEG only cuts payload size (~977KB -> ~141KB), which helps latency at some risk to small-text legibility.

Keep the long edge <= 2576px: an image returned inside a tool_result is rejected rather than downscaled when it exceeds the model's limit.

Install: pip install --upgrade realhands

New config: COMPUTER_USE_PATCH_ALIGN, COMPUTER_USE_CHANGE_THRESHOLD, COMPUTER_USE_MAX_SKIPS. All existing calls keep working unchanged.