From bcca5ed34d31abfd469d139e14bd962c916ff64f Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Thu, 14 May 2026 22:30:12 -0700 Subject: [PATCH] fix(deps): pin brotlicffi so aiohttp can decode Discord's Brotli attachments Discord's CDN serves attachments with Content-Encoding: br. aiohttp's compression_utils tries 'import brotlicffi as brotli' first and falls back to google's Brotli, but Brotli<1.2.0's Decompressor.process() is 1-arg while aiohttp calls it with 2 args (data, max_length). Result: every .txt/.md/.doc uploaded to a Discord-gateway session fails to decode at att.read() with 'Can not decode content-encoding: br' / 'TypeError: process() takes exactly 1 argument (2 given)', the agent never sees the bytes, and falls back to filesystem guessing. Pin brotlicffi==1.2.0.1 in both surfaces: - tools/lazy_deps.py 'platform.discord' tuple: Discord users on the lazy-install path get it on first discord.py import. - pyproject.toml [messaging] extra: users who explicitly install hermes-agent[messaging] (skipping the lazy path) get it eagerly. brotlicffi wins aiohttp's import race regardless of what else is installed (try brotlicffi / except: import brotli), so existing setups that already pulled google's Brotli transitively don't change behavior beyond the bug fix. ~1.5 MB wheel, manylinux/macOS/Windows coverage. E2E verified: round-trip decode of Brotli-compressed payload via aiohttp.compression_utils.brotli succeeds with brotlicffi pinned; same test against Brotli==1.1.0 alone reproduces the reported TypeError. Credit to @Korkyzer for the original diagnosis and fix shape in #15744; the lazy-deps gating layer was added on top to keep brotlicffi out of the install path for users who don't run a Discord gateway. Fixes #12511. Closes #15744. Co-authored-by: Korky --- pyproject.toml | 2 +- scripts/release.py | 1 + tools/lazy_deps.py | 7 ++++++- uv.lock | 27 +++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 982dc01be17..20fecac228e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,7 @@ daytona = ["daytona==0.155.0"] vercel = ["vercel==0.5.7"] hindsight = ["hindsight-client==0.6.1"] dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "pytest-xdist==3.8.0", "pytest-split==0.11.0", "mcp==1.26.0", "ty==0.0.21", "ruff==0.15.10"] -messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"] +messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "brotlicffi==1.2.0.1", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"] cron = [] # croniter is now a core dependency; this extra kept for back-compat slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1", "aiohttp==3.13.3"] matrix = ["mautrix[encryption]==0.21.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"] diff --git a/scripts/release.py b/scripts/release.py index 621ebddec95..d3118bc128e 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -69,6 +69,7 @@ AUTHOR_MAP = { "piyushvp1@gmail.com": "thelumiereguy", "421774554@qq.com": "wuli666", "harish.kukreja@gmail.com": "counterposition", + "korkyzer@gmail.com": "Korkyzer", "1046611633@qq.com": "zhengyn0001", "1095245867@qq.com": "littlewwwhite", "db@project-aeon.com": "db-aeon", diff --git a/tools/lazy_deps.py b/tools/lazy_deps.py index 09347e8281c..258a09ef667 100644 --- a/tools/lazy_deps.py +++ b/tools/lazy_deps.py @@ -116,7 +116,12 @@ LAZY_DEPS: dict[str, tuple[str, ...]] = { # ─── Messaging platforms (lazy-installable on demand) ────────────────── "platform.telegram": ("python-telegram-bot[webhooks]==22.6",), - "platform.discord": ("discord.py[voice]==2.7.1",), + # brotlicffi gives aiohttp a working 2-arg Decompressor.process() for + # Discord CDN's Brotli-encoded attachments. Without it, aiohttp falls + # back to google's `Brotli` package (1-arg API), and any .txt/.md/.doc + # uploaded to the Discord gateway fails to decode at att.read() with + # "Can not decode content-encoding: br" — see #12511 / #15744. + "platform.discord": ("discord.py[voice]==2.7.1", "brotlicffi==1.2.0.1"), "platform.slack": ( "slack-bolt==1.27.0", "slack-sdk==3.40.1", diff --git a/uv.lock b/uv.lock index 72cef3b0cdd..2508637a081 100644 --- a/uv.lock +++ b/uv.lock @@ -537,6 +537,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/f1/90a7b8eda38b7c3a65ca7ee0075bdf310b6b471cb1b95fab6e8994323a50/botocore-1.42.89-py3-none-any.whl", hash = "sha256:d9b786c8d9db6473063b4cc5be0ba7e6a381082307bd6afb69d4216f9fa95f35", size = 14887287, upload-time = "2026-04-13T19:35:56.677Z" }, ] +[[package]] +name = "brotlicffi" +version = "1.2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/b6/017dc5f852ed9b8735af77774509271acbf1de02d238377667145fcee01d/brotlicffi-1.2.0.1.tar.gz", hash = "sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c", size = 478156, upload-time = "2026-03-05T19:54:11.547Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/f9/dfa56316837fa798eac19358351e974de8e1e2ca9475af4cb90293cd6576/brotlicffi-1.2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd", size = 433046, upload-time = "2026-03-05T19:53:46.209Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f5/f8f492158c76b0d940388801f04f747028971ad5774287bded5f1e53f08d/brotlicffi-1.2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5", size = 1541126, upload-time = "2026-03-05T19:53:48.248Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e1/ff87af10ac419600c63e9287a0649c673673ae6b4f2bcf48e96cb2f89f60/brotlicffi-1.2.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac", size = 1541983, upload-time = "2026-03-05T19:53:50.317Z" }, + { url = "https://files.pythonhosted.org/packages/47/c0/80ecd9bd45776109fab14040e478bf63e456967c9ddee2353d8330ed8de1/brotlicffi-1.2.0.1-cp314-cp314t-win32.whl", hash = "sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec", size = 349047, upload-time = "2026-03-05T19:53:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/ab/98/13e5b250236a281b6cd9e92a01ee1ae231029fa78faee932ef3766e1cb24/brotlicffi-1.2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000", size = 385652, upload-time = "2026-03-05T19:53:53.892Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9f/b98dcd4af47994cee97aebac866996a006a2e5fc1fd1e2b82a8ad95cf09c/brotlicffi-1.2.0.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4", size = 432608, upload-time = "2026-03-05T19:53:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7a/ac4ee56595a061e3718a6d1ea7e921f4df156894acffb28ed88a1fd52022/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce", size = 1534257, upload-time = "2026-03-05T19:53:58.667Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/e7410db7f6f56de57744ea52a115084ceb2735f4d44973f349bb92136586/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a", size = 1536838, upload-time = "2026-03-05T19:54:00.705Z" }, + { url = "https://files.pythonhosted.org/packages/a6/75/6e7977d1935fc3fbb201cbd619be8f2c7aea25d40a096967132854b34708/brotlicffi-1.2.0.1-cp38-abi3-win32.whl", hash = "sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187", size = 343337, upload-time = "2026-03-05T19:54:02.446Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ef/e7e485ce5e4ba3843a0a92feb767c7b6098fd6e65ce752918074d175ae71/brotlicffi-1.2.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede", size = 379026, upload-time = "2026-03-05T19:54:04.322Z" }, + { url = "https://files.pythonhosted.org/packages/7f/53/6262c2256513e6f530d81642477cb19367270922063eaa2d7b781d8c723d/brotlicffi-1.2.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851", size = 402265, upload-time = "2026-03-05T19:54:05.858Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d9/d5340b43cf5fbe7fe5a083d237e5338cc1caa73bea523be1c5e452c26290/brotlicffi-1.2.0.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf", size = 406710, upload-time = "2026-03-05T19:54:07.272Z" }, + { url = "https://files.pythonhosted.org/packages/a3/82/dbced4c1e0792efdf23fd90ff6d2a320c64ff4dfef7aacc85c04fde9ddd2/brotlicffi-1.2.0.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4", size = 402787, upload-time = "2026-03-05T19:54:08.73Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6f/534205ba7590c9a8716a614f270c5c2ec419b5b7079b3f9cd31b7b5580de/brotlicffi-1.2.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1", size = 375108, upload-time = "2026-03-05T19:54:10.079Z" }, +] + [[package]] name = "cbor2" version = "5.8.0" @@ -1662,6 +1687,7 @@ mcp = [ ] messaging = [ { name = "aiohttp" }, + { name = "brotlicffi" }, { name = "discord-py", extra = ["voice"] }, { name = "python-telegram-bot", extra = ["webhooks"] }, { name = "qrcode" }, @@ -1742,6 +1768,7 @@ requires-dist = [ { name = "anthropic", marker = "extra == 'anthropic'", specifier = "==0.86.0" }, { name = "asyncpg", marker = "extra == 'matrix'", specifier = "==0.31.0" }, { name = "boto3", marker = "extra == 'bedrock'", specifier = "==1.42.89" }, + { name = "brotlicffi", marker = "extra == 'messaging'", specifier = "==1.2.0.1" }, { name = "croniter", specifier = "==6.0.0" }, { name = "daytona", marker = "extra == 'daytona'", specifier = "==0.155.0" }, { name = "debugpy", marker = "extra == 'dev'", specifier = "==1.8.20" },