-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: supports image compressing #6463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "feat-\u65B0\u589E\u672C\u5730\u56FE\u7247\u9884\u538B\u7F29\u673A\u5236\u5E76\u589E\u5F3A\u56FE\u7247\u89E3\u6790\u5BB9\u9519"
Changes from 7 commits
afa08d9
6fae903
47d82f0
3dce797
7d1e50d
06f6428
b0ba856
1347b3d
e9becb5
ed373a7
8472a9e
684d207
e17ba41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,19 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import asyncio | ||
| import base64 | ||
| import copy | ||
| import datetime | ||
| import io | ||
| import json | ||
| import os | ||
| import time | ||
| import zoneinfo | ||
| from collections.abc import Coroutine | ||
| from dataclasses import dataclass, field | ||
|
|
||
| from PIL import Image as PILImage | ||
|
|
||
| from astrbot.core import logger, sp | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| from astrbot.core.agent.handoff import HandoffTool | ||
| from astrbot.core.agent.mcp_client import MCPTool | ||
|
|
@@ -448,7 +453,7 @@ async def _ensure_img_caption( | |
| caption = await _request_img_caption( | ||
| image_caption_provider, | ||
| cfg, | ||
| req.image_urls, | ||
| [await _compress_image_internal(url) for url in req.image_urls], | ||
|
sourcery-ai[bot] marked this conversation as resolved.
Outdated
|
||
| plugin_context, | ||
| ) | ||
| if caption: | ||
|
|
@@ -458,6 +463,9 @@ async def _ensure_img_caption( | |
| req.image_urls = [] | ||
| except Exception as exc: # noqa: BLE001 | ||
| logger.error("处理图片描述失败: %s", exc) | ||
| req.extra_user_content_parts.append(TextPart(text="图片解析失败")) | ||
| finally: | ||
| req.image_urls = [] | ||
|
Comment on lines
+471
to
+472
|
||
|
|
||
|
|
||
| def _append_quoted_image_attachment(req: ProviderRequest, image_path: str) -> None: | ||
|
|
@@ -523,7 +531,11 @@ async def _process_quote_message( | |
| if prov and isinstance(prov, Provider): | ||
| llm_resp = await prov.text_chat( | ||
| prompt=IMAGE_CAPTION_DEFAULT_PROMPT, | ||
| image_urls=[await image_seg.convert_to_file_path()], | ||
| image_urls=[ | ||
| await _compress_image_internal( | ||
| await image_seg.convert_to_file_path() | ||
| ) | ||
| ], | ||
| ) | ||
| if llm_resp.completion_text: | ||
| content_parts.append( | ||
|
|
@@ -936,7 +948,9 @@ async def build_main_agent( | |
| # media files attachments | ||
| for comp in event.message_obj.message: | ||
| if isinstance(comp, Image): | ||
| image_path = await comp.convert_to_file_path() | ||
| image_path = await _compress_image_internal( | ||
| await comp.convert_to_file_path() | ||
| ) | ||
|
Soulter marked this conversation as resolved.
Outdated
|
||
| req.image_urls.append(image_path) | ||
| req.extra_user_content_parts.append( | ||
| TextPart(text=f"[Image Attachment: path {image_path}]") | ||
|
|
@@ -963,7 +977,9 @@ async def build_main_agent( | |
| for reply_comp in comp.chain: | ||
| if isinstance(reply_comp, Image): | ||
| has_embedded_image = True | ||
| image_path = await reply_comp.convert_to_file_path() | ||
| image_path = await _compress_image_internal( | ||
| await reply_comp.convert_to_file_path() | ||
| ) | ||
| req.image_urls.append(image_path) | ||
| _append_quoted_image_attachment(req, image_path) | ||
| elif isinstance(reply_comp, File): | ||
|
|
@@ -1164,3 +1180,47 @@ async def build_main_agent( | |
| provider=provider, | ||
| reset_coro=reset_coro if not apply_reset else None, | ||
| ) | ||
|
|
||
| # 异步图片压缩 | ||
| def _do_compress_sync(data: bytes, temp_dir: str) -> str: | ||
| """同步执行图片压缩逻辑,由 asyncio.to_thread 调用""" | ||
|
|
||
| img = PILImage.open(io.BytesIO(data)) | ||
| if img.mode in ("RGBA", "P"): | ||
| img = img.convert("RGB") | ||
| max_size = 1280 | ||
| if max(img.size) > max_size: | ||
| img.thumbnail((max_size, max_size), PILImage.Resampling.LANCZOS) | ||
|
|
||
| timestamp = int(time.time() * 1000) | ||
|
Soulter marked this conversation as resolved.
Outdated
|
||
| save_path = os.path.join(temp_dir, f"compressed_{timestamp}.jpg") | ||
|
sourcery-ai[bot] marked this conversation as resolved.
Outdated
|
||
| img.save(save_path, "JPEG", quality=85, optimize=True) | ||
|
Soulter marked this conversation as resolved.
Outdated
|
||
| return save_path | ||
|
|
||
| # 压缩用户上传的大体积图片 未来可以提取为通用工具 | ||
| async def _compress_image_internal(url_or_path: str) -> str: | ||
|
sourcery-ai[bot] marked this conversation as resolved.
Outdated
|
||
| try: | ||
| data = None | ||
| # 若为远程图片则直接返回原值 无需压缩 | ||
| if url_or_path.startswith("http"): | ||
| return url_or_path | ||
| elif url_or_path.startswith("data:image"): | ||
| header, encoded = url_or_path.split(",", 1) | ||
|
|
||
| data = base64.b64decode(encoded) | ||
| elif os.path.exists(url_or_path): | ||
| if os.path.getsize(url_or_path) < 1024 * 1024: | ||
| return url_or_path | ||
| with open(url_or_path, "rb") as f: | ||
| data = f.read() | ||
|
|
||
| if not data: | ||
| return url_or_path | ||
|
|
||
| temp_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data/temp") | ||
|
Soulter marked this conversation as resolved.
Outdated
Soulter marked this conversation as resolved.
Outdated
|
||
|
|
||
| # 使用 asyncio.to_thread 将同步阻塞的图片处理任务交给线程池 | ||
| return await asyncio.to_thread(_do_compress_sync, data, temp_dir) | ||
|
Soulter marked this conversation as resolved.
Outdated
|
||
|
|
||
| except Exception as e: | ||
| logger.error("图片压缩失败: %s", e) | ||
| return url_or_path | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为了支持
_do_compress_sync函数中建议的uuid.uuid4().hex用法,需要导入uuid模块。