#23662: fix: cache sanitized images to avoid redundant re-processing per turn
agents
size: M
## Problem
Fixes #23590
When images are sent in a conversation (e.g. via Telegram), every image in session history gets re-processed on every subsequent turn — decoded from base64, metadata read, and potentially resized. In a conversation with 3 images and 10 follow-up messages, that's 30 redundant resize operations instead of 3.
The logs fill with repeated identical entries:
```
[agents/tool-images] Image resized to fit limits: 875x1280px 106.0KB -> 109.4KB (--3.2%)
[agents/tool-images] Image resized to fit limits: 1280x772px 136.0KB -> 137.1KB (--0.8%)
```
## Fix
Add a bounded in-memory LRU cache (max 64 entries) in `resizeImageBase64IfNeeded` that stores sanitization results keyed by a fast composite key (base64 prefix + length + sanitization parameters).
On cache hit, the function returns immediately without:
- Decoding base64 to Buffer
- Reading image metadata (dimensions, format)
- Running resize operations
The cache is bounded to prevent unbounded memory growth in very long sessions — when full, the oldest entry is evicted (Map insertion order).
## Cache key design
Uses `base64.slice(0, 64) + ":" + base64.length + ":" + maxDimensionPx + ":" + maxBytes` for O(1) lookups. This is sufficient because:
- Same base64 string = identical image content (the string IS the content)
- The prefix + length combo has effectively zero collision risk
- No expensive hashing needed for large images
## Testing
Added test that verifies `getImageMetadata` is NOT called on the second pass of the same image through `sanitizeToolResultImages`.
```
✓ src/agents/tool-images.cache.test.ts (1 test) 2ms
```
---
*This PR was AI-assisted (per CONTRIBUTING.md guidelines).*
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds bounded LRU cache (max 64 entries) to `resizeImageBase64IfNeeded` that prevents redundant image processing across conversation turns. Cache uses composite key (base64 prefix + length + sanitization params) for O(1) lookups without expensive hashing. When cache hits, the function returns immediately without decoding base64, reading metadata, or resizing. Implementation correctly caches both pass-through (no resize) and resized results, with proper Map-based LRU eviction when at capacity.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- Clean performance optimization with proper bounds, correct LRU eviction logic, effective test coverage validating cache behavior, and no changes to external API surface. The cache key design is sound (prefix + length creates unique identifiers for base64 images), bounded size prevents memory issues, and the implementation only affects the internal performance path.
- No files require special attention
<sub>Last reviewed commit: 2561966</sub>
<!-- greptile_other_comments_section -->
<sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#23706: perf: cache image resize results to avoid redundant processing (#23...
by echoVic · 2026-02-22
91.0%
#23639: fix(agents): stop re-resizing session history images on every turn ...
by yinghaosang · 2026-02-22
90.7%
#9598: fix(agents): check base64 string length against 5MB API limit
by BlockBB · 2026-02-05
82.8%
#19135: fix(agents): prune excess images from conversation history (#19099)
by pierreeurope · 2026-02-17
77.8%
#2958: fix(media): wire tools.media.image.maxBytes config to image processin…
by shamsulalam1114 · 2026-01-27
77.1%
#18219: fix: validate base64 image data before sending to LLM APIs
by Grynn · 2026-02-16
74.7%
#8172: fix(sessions_list): strip base64 image data to prevent context over...
by Flamrru · 2026-02-03
74.3%
#5817: fix: strip old images during compaction to prevent 413 session bloat
by jduartedj · 2026-02-01
73.1%
#9026: fix(session-memory): sanitize content to prevent binary data in mem...
by Flamrru · 2026-02-04
73.0%
#20913: fix: intercept Discord embed images to enforce mediaMaxMb
by MumuTW · 2026-02-19
72.4%