fix(qqbot): add .md and other doc extensions to extract_media() regex - #22492
fix(qqbot): add .md and other doc extensions to extract_media() regex#22492Allonz wants to merge 12 commits into
Conversation
- Add media file upload support to _send_qqbot function
- Support chat_type detection from target format (c2c:/group:/guild:)
- Upload media via QQ Bot v2 API (/v2/users/{openid}/files, /v2/groups/{group_openid}/files)
- Map file extensions to QQ Bot file_type (1=image, 2=video, 3=voice, 4=file)
- Include media in message payload via 'file_info' field
- Update error messages to include qqbot in supported platforms
- Update schema description with qqbot target format examples
… attempts Fix 4 issues in QQBot adapter reconnect logic: 1. [P0] _listen_loop exit now calls _set_fatal_error() to notify Gateway When reconnect attempts are exhausted, the adapter now sets a retryable fatal error so _platform_reconnect_watcher can take over. Previously the listen loop would silently die, leaving QQBot in a zombie state where the process is alive but no messages are received. 2. [P1] CLOSED/ERROR WS events now raise QQCloseError instead of plain RuntimeError, preserving close code/reason for proper error classification. 3. [P1] Added 15s cooldown after reconnect failure to give QQ server time to clean up the old session before the next attempt. 4. [P2] Moved _heartbeat_interval reset inside _reconnect() try block so it only resets after a successful connection, not on failure.
…a support) Keep both QQBot and Feishu native media delivery blocks. Preserve QQBot's full chat_type routing and base64 file upload logic from HEAD, while incorporating Feishu's media support and thread_id parameter from upstream/main. Update error/warning strings to list both platforms.
…ping/pong Bug 5: _reconnect() opens a new WebSocket but never recreates _heartbeat_task. The old heartbeat task is orphaned on the dead connection — no heartbeat is sent on the new one. QQ server closes the connection after 60s with code=None (no close code because the server drops it cleanly). Reconnect succeeds, but the death loop repeats every 60s: connect → 60s silence → disconnect → reconnect → repeat. Changes: - _open_ws(): add heartbeat=20 to aiohttp ClientWebSocketResponse for WS-level ping/pong, preventing idle disconnects at the transport layer - _reconnect(): cancel old _heartbeat_task before opening new WebSocket - _reconnect(): create_task(_heartbeat_loop()) after successful reconnect
MEDIA: tag extraction silently ignored .md, .json, .xml, .html, .log, .yaml, .yml, and .toml files because the extension allowlist in BasePlatformAdapter.extract_media() didn't include these formats. This prevented the gateway's _deliver_media_tags_streaming() and the send_message tool from delivering document files produce d by agents (e.g. markdown reports, logs, config files) via QQBot and other platforms that use send_document(). Fix: add md|json|xml|html?|log|yaml|yml|toml to the regex extension alternation. All 183 existing tests pass with no regressions. Verified: - MEDIA:/path/to/file.md → extracted correctly - MEDIA:/path/to/file.json → extracted correctly - Existing formats (pdf, docx, png, etc.) → no regression
teknium1
left a comment
There was a problem hiding this comment.
Thanks for tracing the original MEDIA extraction failure. The main extension-list work was superseded by commit 781604ce4 (fix(gateway): unify MEDIA: extraction extension set + close the unknown-ext black hole), which replaced the inline regex with the shared MEDIA_DELIVERY_EXTS contract at gateway/platforms/base.py:1432-1474 and made extract_media() consume it at gateway/platforms/base.py:3618-3622.
Problems
- Current main still omits
.logand.tomlfromMEDIA_DELIVERY_EXTS(gateway/platforms/base.py:1432-1449), although both are recognized document types atgateway/platforms/base.py:1338-1343. The remaining bug is therefore narrower than the PR's original list. - The PR's literal-regex change cannot be salvaged directly without bypassing the shared source of truth introduced by
781604ce4. - The bundled QQBot media/reconnect work needs separate re-evaluation: current main already has a chunked native-media implementation in
gateway/platforms/qqbot/adapter.py:2840-2914.
Suggested changes
- Re-scope the extraction fix to add
.logand.tomltoMEDIA_DELIVERY_EXTS, with parameterized regression coverage intests/gateway/test_platform_base.py.
Automated hermes-sweeper review.
| # and quoted/backticked paths for LLM-formatted outputs. | ||
| media_pattern = re.compile( | ||
| r'''[`"']?MEDIA:\s*(?P<path>`[^`\n]+`|"[^"\n]+"|'[^'\n]+'|(?:~/|/)\S+(?:[^\S\n]+\S+)*?\.(?:png|jpe?g|gif|webp|mp4|mov|avi|mkv|webm|ogg|opus|mp3|wav|m4a|flac|epub|pdf|zip|rar|7z|docx?|xlsx?|pptx?|txt|csv|apk|ipa)(?=[\s`"',;:)\]}]|$)|\S+)[`"']?''' | ||
| r'''[`"']?MEDIA:\s*(?P<path>`[^`\n]+`|"[^"\n]+"|'[^'\n]+'|(?:~/|/)\S+(?:[^\S\n]+\S+)*?\.(?:png|jpe?g|gif|webp|mp4|mov|avi|mkv|webm|ogg|opus|mp3|wav|m4a|flac|epub|pdf|zip|rar|7z|docx?|xlsx?|pptx?|txt|csv|apk|ipa|md|json|xml|html?|log|yaml|yml|toml)(?=[\s`"',;:)\]}]|$)|\S+)[`"']?''' |
There was a problem hiding this comment.
Current main replaced this literal alternation with the shared MEDIA_DELIVERY_EXTS contract (gateway/platforms/base.py:1432-1474, commit 781604ce4). Please re-scope the salvage to add the remaining .log and .toml entries there, rather than restoring a second extension list.
Summary
MEDIA: tag extraction silently ignored .md, .json, .xml, .html, .log, .yaml, .yml, and .toml files because the extension allowlist in BasePlatformAdapter.extract_media() didn't include these formats.
This prevented the gateway's _deliver_media_tags_streaming() and the send_message tool from delivering document files produced by agents (e.g. markdown reports, logs, config files) via QQBot and other platforms that use send_document().
Root Cause
The regex in gateway/platforms/base.py:1928 had a hardcoded extension alternation that only matched:
png|jpe?g|gif|webp|mp4|mov|avi|mkv|webm|ogg|opus|mp3|wav|m4a|flac|
epub|pdf|zip|rar|7z|docx?|xlsx?|pptx?|txt|csv|apk|ipa
Common document/code/text formats were missing - most notably .md (markdown), which Hermes agents frequently generate.
Fix
Added md|json|xml|html?|log|yaml|yml|toml to the regex extension alternation (1 line change in gateway/platforms/base.py).
Test Results
Existing tests (no regression)
Manual verification
All new extensions extracted correctly:
Existing extensions still work:
Impact
Low risk - purely additive: no existing extensions removed, no logic changed.