Skip to content

fix(core): downscale oversized images#7807

Merged
Soulter merged 3 commits intoAstrBotDevs:masterfrom
bugkeep:bugfix/7791-downscale-images-2048
Apr 26, 2026
Merged

fix(core): downscale oversized images#7807
Soulter merged 3 commits intoAstrBotDevs:masterfrom
bugkeep:bugfix/7791-downscale-images-2048

Conversation

@bugkeep
Copy link
Copy Markdown
Contributor

@bugkeep bugkeep commented Apr 25, 2026

Fixes #7791.

Problem

  • compress_image() only triggered when the source file was larger than 1MB, so a dimension-oversized but highly-compressible image (e.g. solid-color PNG) could bypass downscaling and then be rejected by upstream vision limits (2048x2048).

Change

  • When the source is below the size threshold, probe image dimensions and still downscale if max(width, height) > max_size.
  • Applies to both local paths and data:image/... payloads.

Tests

  • uv run pytest -q tests/unit/test_media_utils_compress_image.py
  • uv run ruff format .
  • uv run ruff check .

Summary by Sourcery

Ensure oversized images are downscaled even when their file size is below the compression threshold.

Bug Fixes:

  • Prevent large-dimension but small-file-size images from bypassing downscaling and being rejected by upstream vision size limits.

Tests:

  • Add unit tests covering downscaling of small-file local images, skipping compression for small images within size limits, and handling of oversized data URL images.

@auto-assign auto-assign Bot requested review from Fridemn and Raven95676 April 25, 2026 15:00
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend labels Apr 25, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The _exceeds_max_size_bytes and _exceeds_max_size_path helpers duplicate the same logic; consider consolidating them into a single helper that accepts a file-like object or a more generic input to reduce repetition.
  • Both helpers swallow all exceptions and return False, which could silently mask real image parsing errors; it may be safer to either log these failures or narrow the exception types so unexpected issues surface.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `_exceeds_max_size_bytes` and `_exceeds_max_size_path` helpers duplicate the same logic; consider consolidating them into a single helper that accepts a file-like object or a more generic input to reduce repetition.
- Both helpers swallow all exceptions and return `False`, which could silently mask real image parsing errors; it may be safer to either log these failures or narrow the exception types so unexpected issues surface.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances the image compression logic to account for image dimensions, ensuring that images exceeding a specified size are compressed even if their file size is small. It also introduces unit tests to verify these changes. The review feedback recommends refactoring the two new helper functions into a single, more versatile helper to eliminate code duplication.

Comment thread astrbot/core/utils/media_utils.py Outdated
Comment on lines +440 to +452
def _exceeds_max_size_bytes(raw: bytes) -> bool:
try:
with PILImage.open(io.BytesIO(raw)) as opened_img:
return max(opened_img.size) > max_size
except Exception: # noqa: BLE001
return False

def _exceeds_max_size_path(path: Path) -> bool:
try:
with PILImage.open(path) as opened_img:
return max(opened_img.size) > max_size
except Exception: # noqa: BLE001
return False
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.

medium

The two helper functions _exceeds_max_size_bytes and _exceeds_max_size_path perform nearly identical logic. Per the general rules, these should be refactored into a single shared helper function to avoid code duplication. Since PILImage.open accepts both file-like objects and path-like objects, a single helper can handle both cases. Additionally, ensure that this new attachment handling logic is accompanied by corresponding unit tests.

Suggested change
def _exceeds_max_size_bytes(raw: bytes) -> bool:
try:
with PILImage.open(io.BytesIO(raw)) as opened_img:
return max(opened_img.size) > max_size
except Exception: # noqa: BLE001
return False
def _exceeds_max_size_path(path: Path) -> bool:
try:
with PILImage.open(path) as opened_img:
return max(opened_img.size) > max_size
except Exception: # noqa: BLE001
return False
def _exceeds_max_size(source: bytes | Path) -> bool:
try:
fp = io.BytesIO(source) if isinstance(source, bytes) else source
with PILImage.open(fp) as opened_img:
return max(opened_img.size) > max_size
except Exception: # noqa: BLE001
return False
References
  1. When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
  2. New functionality, such as handling attachments, should be accompanied by corresponding unit tests.

Comment thread astrbot/core/utils/media_utils.py Outdated
_header, encoded = url_or_path.split(",", 1)
data = base64.b64decode(encoded)
if len(data) < min_file_size_bytes:
if len(data) < min_file_size_bytes and not _exceeds_max_size_bytes(data):
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.

medium

Update the call to use the refactored helper function.

Suggested change
if len(data) < min_file_size_bytes and not _exceeds_max_size_bytes(data):
if len(data) < min_file_size_bytes and not _exceeds_max_size(data):
References
  1. When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.

Comment thread astrbot/core/utils/media_utils.py Outdated
if local_path.stat().st_size < min_file_size_bytes:
if (
local_path.stat().st_size < min_file_size_bytes
and not _exceeds_max_size_path(local_path)
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.

medium

Update the call to use the refactored helper function.

Suggested change
and not _exceeds_max_size_path(local_path)
and not _exceeds_max_size(local_path)
References
  1. When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 26, 2026
@Soulter Soulter merged commit bbda1e6 into AstrBotDevs:master Apr 26, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend lgtm This PR has been approved by a maintainer size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 图片压缩无效,当遇到大于2048*2048的图时会无法处理

2 participants