diff --git a/CHANGELOG.md b/CHANGELOG.md index d61f606daf..1b78a05d27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,12 @@ This section accumulates work toward the **2.3.0** milestone ### Fixed +- **channels:** WeCom no longer drops images between 2 MB and 10 MB. The + adapter capped image uploads at 2 MB — the voice limit in the same protocol — + so a larger chart was skipped before upload and the turn arrived without it. + The cap now matches the platform's 10 MB for images; the 20 MB file limit is + unchanged. ([#8174]) + - **threads:** Deleting a conversation on Windows no longer fails with `Failed to delete local thread data.` when the workspace holds a file whose path exceeds `MAX_PATH`. The thread directory is now resolved through the @@ -16341,3 +16347,4 @@ with **180 merged pull requests** since the first 2.0 milestone tag. [#8126]: https://github.com/bytedance/deer-flow/pull/8126 [#8131]: https://github.com/bytedance/deer-flow/pull/8131 [#8151]: https://github.com/bytedance/deer-flow/pull/8151 +[#8174]: https://github.com/bytedance/deer-flow/pull/8174 diff --git a/CHANGELOG_zh.md b/CHANGELOG_zh.md index ea21e9c16b..a82b8079e2 100644 --- a/CHANGELOG_zh.md +++ b/CHANGELOG_zh.md @@ -26,6 +26,9 @@ ### 修复 +- **通道:** 企业微信不再丢弃 2–10 MB 的图片。此前适配器把图片上限定为 2 MB(同一协议中语音的限制), + 超出即在上传前被跳过,消息到达时缺少图片。现在图片上限与平台的 10 MB 对齐;普通文件 20 MB 的限制不变。([#8174]) + - **通道:** WeCom 群聊中 `@机器人 /new` 这类带提及的命令现在按命令执行。WeCom 会把提及保留在 正文里,而命令识别要求裸的前导 `/`,于是群里的 `/new`、`/status` 被当成聊天轮次。现在仅在命令 识别时跳过前导提及;普通聊天保留提及,`/connect` 行为不变。与飞书、钉钉的处理一致。([#8118]) @@ -12872,3 +12875,4 @@ DeerFlow 2.0 是围绕"超级智能体"框架的彻底重写,核心包含子 [#8064]: https://github.com/bytedance/deer-flow/pull/8064 [#8118]: https://github.com/bytedance/deer-flow/pull/8118 [#8126]: https://github.com/bytedance/deer-flow/pull/8126 +[#8174]: https://github.com/bytedance/deer-flow/pull/8174 diff --git a/backend/app/channels/wecom.py b/backend/app/channels/wecom.py index a20bc0f4e8..8a684ec525 100644 --- a/backend/app/channels/wecom.py +++ b/backend/app/channels/wecom.py @@ -447,7 +447,11 @@ class WeComChannel(Channel): return False media_type = "image" if attachment.is_image else "file" - size_limit = 2 * 1024 * 1024 if attachment.is_image else 20 * 1024 * 1024 + # aibot media upload limits: image 10 MB, voice 2 MB, video 10 MB, + # file 20 MB (https://developer.work.weixin.qq.com/document/path/101917). + # The image cap used to be 2 MB — the voice limit — which dropped every + # 2-10 MB image before it was uploaded. + size_limit = 10 * 1024 * 1024 if attachment.is_image else 20 * 1024 * 1024 if attachment.size > size_limit: logger.warning( "[WeCom] %s too large (%d bytes), skipping: %s", diff --git a/backend/tests/test_channel_file_attachments.py b/backend/tests/test_channel_file_attachments.py index 37c462ee8e..6da165a005 100644 --- a/backend/tests/test_channel_file_attachments.py +++ b/backend/tests/test_channel_file_attachments.py @@ -1822,3 +1822,64 @@ def test_embedded_upload_retries_claim_collision(tmp_path, monkeypatch, channel_ advertised = "/mnt/user-data/uploads/report_2.pdf" assert advertised in result.text assert synced == ([] if mounted else [(advertised, payload)]) + + +class TestWecomMediaSizeLimits: + """WeCom's aibot media upload accepts images up to 10 MB. + + The adapter used to cap images at 2 MB — the *voice* limit in the same + protocol — and silently skipped anything larger before uploading it, so a + 2-10 MB chart never reached the user. See + https://developer.work.weixin.qq.com/document/path/101917 + """ + + @staticmethod + def _channel(): + from types import SimpleNamespace + + from app.channels.wecom import WeComChannel + + channel = WeComChannel(MessageBus(), {}) + channel._ws_client = SimpleNamespace(reply=AsyncMock()) + channel._upload_media_ws = AsyncMock(return_value="media-1") + return channel + + def test_image_above_two_megabytes_reaches_the_uploader(self, tmp_path): + channel = self._channel() + msg = _file_reply(tmp_path, channel_name="wecom") + channel._ws_frames[msg.thread_ts] = {"body": {"msgid": msg.thread_ts}} + channel._ws_stream_ids[msg.thread_ts] = "stream-1" + + path = tmp_path / "chart.png" + path.write_bytes(b"\x89PNG\r\n\x1a\n") + attachment = ResolvedAttachment( + "/mnt/user-data/outputs/chart.png", + path, + "chart.png", + "image/png", + 5 * 1024 * 1024, + True, + ) + + assert _run(channel.send_file(msg, attachment)) is True + channel._upload_media_ws.assert_awaited_once() + + def test_image_above_the_platform_limit_is_still_skipped(self, tmp_path): + channel = self._channel() + msg = _file_reply(tmp_path, channel_name="wecom") + channel._ws_frames[msg.thread_ts] = {"body": {"msgid": msg.thread_ts}} + channel._ws_stream_ids[msg.thread_ts] = "stream-1" + + path = tmp_path / "huge.png" + path.write_bytes(b"\x89PNG\r\n\x1a\n") + attachment = ResolvedAttachment( + "/mnt/user-data/outputs/huge.png", + path, + "huge.png", + "image/png", + 11 * 1024 * 1024, + True, + ) + + assert _run(channel.send_file(msg, attachment)) is False + channel._upload_media_ws.assert_not_awaited()