Skip to content

fix: strip base64 image blobs from session DB to prevent context overflow - #31705

Closed
i-xoxol wants to merge 1 commit into
NousResearch:mainfrom
i-xoxol:fix/photo-as-file-session-overflow
Closed

fix: strip base64 image blobs from session DB to prevent context overflow#31705
i-xoxol wants to merge 1 commit into
NousResearch:mainfrom
i-xoxol:fix/photo-as-file-session-overflow

Conversation

@i-xoxol

@i-xoxol i-xoxol commented May 24, 2026

Copy link
Copy Markdown

Problem

Sending a photo as an uncompressed file (document) in Telegram caused session context overflow. The native-vision path encodes the image as a data:image/...;base64,... URL and persists it in the SQLite messages table. get_messages_as_conversation() decodes and re-injects the full blob on every subsequent turn, growing the outgoing API request by ~7 MB per image until the provider rejects it.

Normal compressed photos are worse than URLs but still manageable; uncompressed documents (5–20 MB originals) hit the limit quickly.

Fix

Add SessionDB._strip_image_blobs(), called from _encode_content(), that replaces data: base64 blobs in multimodal content lists with a short text placeholder before the JSON is written to SQLite.

  • Plain https:// URLs (Telegram CDN etc.) are not affected
  • The local cache path is already present in the companion text part ([Image attached at: /tmp/...]), so the model can re-read the image via vision_analyze if needed
  • Existing sessions with blobs already stored are still handled by _strip_historical_media in the context compressor (existing safety net, now a last resort rather than first line of defence)

Tests

12 new unit tests in tests/gateway/test_session_db_strip_image_blobs.py covering:

  • scalar passthrough
  • no-image fast path (identity / no copy)
  • https:// URL untouched
  • single / multiple data: blobs replaced
  • mixed data:/https: content
  • input not mutated
  • idempotency (double-strip is a no-op)
  • _encode_content integration
  • encode→decode roundtrip never restores a blob

…flow

When a user sends a photo as a file (uncompressed document), the native-vision
path encodes the full image as a data: base64 URL and stores it in the session
DB. On every subsequent turn, load_transcript decodes and re-injects the full
blob into conversation history, pushing outgoing API requests past provider
body-size or context-window limits ('session overflow').

Fix: add SessionDB._strip_image_blobs() called by _encode_content() to replace
data: base64 blobs with a short text placeholder before the content list is
serialised to SQLite. The local file path hint is already present in the
companion text part (added by build_native_content_parts), so the model can
re-access the image via vision_analyze without any round-trip penalty.

- Plain https:// URLs (Telegram CDN etc.) are unaffected
- _strip_historical_media in context_compressor still fires for any blobs that
  entered history before this fix (existing sessions)
- 12 new tests cover the strip logic and encode/decode roundtrip
Copilot AI review requested due to automatic review settings May 24, 2026 22:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds protection against session DB bloat by stripping base64 data: image blobs from multimodal message content before persistence, with accompanying unit tests to prevent regressions.

Changes:

  • Introduces SessionDB._strip_image_blobs() to replace data: image URLs with a short placeholder.
  • Updates _encode_content() to call _strip_image_blobs() prior to JSON serialization.
  • Adds tests covering stripping behavior and _encode_content() integration.

Reviewed changes

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

File Description
tests/gateway/test_session_db_strip_image_blobs.py Adds unit tests for stripping base64 image blobs and verifying encoded/decoded content doesn’t reintroduce them.
hermes_state.py Implements _strip_image_blobs() and integrates it into _encode_content() to prevent persisting large base64 data URLs.

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

Comment thread hermes_state.py
Comment on lines +1449 to +1452
new_parts.append({
"type": "text",
"text": "[Attached image — base64 stripped for storage; re-read from the path above if needed]",
})
Comment thread hermes_state.py
Comment on lines +1432 to +1456
if not any(
isinstance(p, dict)
and p.get("type") == "image_url"
and isinstance(p.get("image_url"), dict)
and str(p["image_url"].get("url", "")).startswith("data:")
for p in content
):
return content # fast path — nothing to strip

new_parts: list = []
for p in content:
if (
isinstance(p, dict)
and p.get("type") == "image_url"
and isinstance(p.get("image_url"), dict)
and str(p["image_url"].get("url", "")).startswith("data:")
):
new_parts.append({
"type": "text",
"text": "[Attached image — base64 stripped for storage; re-read from the path above if needed]",
})
else:
new_parts.append(p)
return new_parts

Comment thread hermes_state.py
Comment on lines 1477 to 1482
if content is None or isinstance(content, (str, bytes, int, float)):
return content
if isinstance(content, list):
content = cls._strip_image_blobs(content)
try:
return cls._CONTENT_JSON_PREFIX + json.dumps(content)
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery tool/vision Vision analysis and image generation labels May 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #19962 (open) which also strips base64 data-URLs at persistence boundaries. This PR takes a similar approach via _strip_image_blobs() in _encode_content(). Also related to #28446 (feature request for max_vision_images_in_context config).

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused regression coverage. This is an automated hermes-sweeper review: the reported persistence/replay overflow is already prevented on current main.

  • gateway/run.py:18870 routes native image attachments through AIAgent.run_conversation().
  • run_agent.py:1870-1896 reduces multipart content to text plus [screenshot] before calling SessionDB.append_message(), so data:image/...;base64,... payloads are not persisted for later replay.
  • hermes_state.py:4368 replays the persisted content column, which therefore no longer contains the original image blob on this route.

Closing as implemented on main.

@teknium1 teknium1 closed this Jul 13, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 13, 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 sweeper:implemented-on-main Sweeper: behavior already present on current main 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.

4 participants