Skip to content

fix(agent): accept pixel-correct image downscale when bytes grow (#48013) - #49140

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-f892ee72
Jun 19, 2026
Merged

fix(agent): accept pixel-correct image downscale when bytes grow (#48013)#49140
teknium1 merged 1 commit into
mainfrom
hermes/hermes-f892ee72

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Image-shrink recovery now accepts a pixel-correct downscale even when the re-encoded PNG grows in bytes — fixing the image-dimension 400 session brick (#48013).

Root cause: try_shrink_image_parts_in_messages gated accept/reject on byte size (len(resized) >= len(url) → discard), but the failing constraint was pixel dimensions. A downscaled Retina screenshot routinely re-encodes to more PNG bytes than the original, so the pixel-correct result was thrown away, the image stayed oversized, and the session wedged forever on the Anthropic many-image 2000px path.

Changes

  • agent/conversation_compression.py: track which constraint triggered the shrink (bytes vs dimension) and gate accept on that same axis. Dimension path accepts on pixel-cap satisfaction regardless of byte growth; bytes path additionally re-checks the per-side cap (a very-high-aspect image can byte-shrink yet stay over-cap); unshrinkable_oversized extended to the pixel axis so a partial shrink doesn't burn the one-shot retry.
  • tests/run_agent/repro_48013_image_shrink_brick.py: runnable real-Pillow proof (no mocks) reproducing the issue's per-image table.
  • tests/run_agent/test_image_shrink_recovery.py: 21 tests covering dimension accept-on-byte-growth, dimension-failure-blocks-retry, mixed partial-progress, and the bytes-path-still-over-cap regression.

Single shared call site → fixes CLI, TUI, and gateway alike.

Validation

Byte gate (main) This fix
2097x1476 screenshot (16 KB, >2000px) changed=False, left at 2097px (BRICK) changed=True, 1048x738 (OK)
repro proof exit code 1 (FAIL: 3 bricked) 0 (PASS: 5/5 under cap)
test_image_shrink_recovery.py + repro 22/22 passing

The proof is discriminating: reverting conversation_compression.py to current main flips it to FAIL: 3 image(s) still over the pixel cap.

Salvage / credit

Salvaged from #49124 by @alt-glitch (Siddharth Balyan), cherry-picked onto current main with authorship preserved. Builds on the approach in #48035 by @Tranquil-Flow (same core idea: track the binding constraint). Supersedes #48046 (@kyssta-exe) — that one-liner predated the max_dimension plumbing and would revert #45979's cap parsing.

Closes #48013

Infographic

image-shrink-recovery-binding-constraint

)

The image-too-large reactive shrink (try_shrink_image_parts_in_messages)
conflated two independent constraints: it always rejected a resize whose
re-encoded bytes were >= the original, even when the shrink was driven by a
PIXEL-DIMENSION cap (Anthropic many-image 2000px) rather than the byte budget.
Downscaled screenshot PNGs routinely re-encode LARGER in bytes, so the
dimension-correct result was discarded and the image left oversized -> the
provider re-rejected on retry and the session wedged forever.

Fix: track which constraint triggered the shrink (bytes vs dimension) and gate
the accept on the SAME axis.
  * dimension path: accept the result as long as it is now within max_dimension,
    regardless of byte size (verify via Pillow; fall back to the byte gate only
    when the re-encode can't be decoded).
  * bytes path: still require bytes to shrink, but ALSO re-check the per-side cap
    when it's active — _resize_image_for_vision returns a best-effort, possibly
    over-cap blob when it exhausts its halving budget on a very-high-aspect
    image, so a byte-shrink alone can leave it over the dimension cap and
    re-brick on retry.
Extend the unshrinkable-oversized guard to the pixel axis so a partial shrink
doesn't burn the one-shot retry.

Single shared agent path -> fixes CLI, TUI, and gateway alike.

Adds a real-Pillow runnable proof (repro_48013_image_shrink_brick.py) that
reproduces the issue's per-image table (bricks 3/5 before, passes 5/5 after)
plus unit invariants for the dimension and bytes accept/reject paths,
partial-progress accounting, and the bytes-path still-over-cap regression
surfaced by adversarial review.

Closes #48013
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-f892ee72 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11161 on HEAD, 11157 on base (🆕 +4)

🆕 New issues (4):

Rule Count
unresolved-attribute 2
unresolved-import 2
First entries
run_agent.py:2971: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
tests/run_agent/repro_48013_image_shrink_brick.py:41: [unresolved-import] unresolved-import: Cannot resolve imported module `PIL`
tests/run_agent/repro_48013_image_shrink_brick.py:33: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues (1):

Rule Count
invalid-assignment 1
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`

Unchanged: 5846 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/vision Vision analysis and image generation provider/anthropic Anthropic native Messages API P2 Medium — degraded but workaround exists labels Jun 19, 2026
@teknium1
teknium1 merged commit 990273d into main Jun 19, 2026
35 checks passed
@teknium1
teknium1 deleted the hermes/hermes-f892ee72 branch June 19, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API tool/vision Vision analysis and image generation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Image-dimension 400 bricks sessions: shrink recovery discards a pixel-correct downscale when re-encoded PNG bytes grow (survives #45979)

2 participants