Conversation
…voice overrides The shared media dispatcher (gateway/platforms/base.py _send_one, gateway/run_notifications.py, gateway/run_turn.py) passes is_voice= to adapter.send_voice() for every non-image audio attachment. Three plugin adapters overrode send_voice with a narrower signature and no **kwargs, so the call raised TypeError before any upload happened and MEDIA audio replies were dropped silently (run_turn.py wraps its call site in suppress(Exception)). Affected adapters: - Matrix (no voice bubbles or audio attachments delivered) - Mattermost - LINE Fix: accept-and-ignore **kwargs in the three overrides, matching the base adapter's signature tolerance (routing is decided by the caller). Add an invariant test asserting the overrides tolerate the dispatcher's exact call shape (red on base, green with fix). Verified live on Matrix: TTS voice replies deliver as native audio after the fix.
|
Independent reproduction on current main ( Two boundary notes that may help the maintainer pick the carrier:
The test here asserts the right invariant: the dispatcher contract (an adapter's override must tolerate the kwargs the shared media path passes), not the individual signatures — that is what broke, and what the silent |
Same bug class as the Matrix/Mattermost/LINE send_voice overrides: WeixinAdapter.send_video and send_voice define narrow signatures without **kwargs, so the shared media dispatcher's is_voice= kwarg raises TypeError before upload and audio/video MEDIA attachments are silently dropped (gateway/platforms/weixin.py). Adds **kwargs to both overrides, matching the base adapter's signature tolerance.
|
Two updates:
|
|
Thanks @KeyArgo — the census and dispatcher-contract framing are exactly right. One timing note: the Weixin override was added to this PR in d599158 just before your comment landed, so all four are covered here and no exclusion needs stating. @101381 and #113645 also carry the Weixin fix — happy to defer to whichever carrier the maintainers prefer; if one of those merges first, the Weixin commit here can be dropped without affecting the other three adapters. |
|
Independent verification of this patch on current Method: applied this diff to a clean checkout, then called Without the patch — all four adapters, and the warning matches the report verbatim: With the patch applied — 4/4 The new test file reproduces its claimed RED/GREEN as well: Two review notes, both small:
For the queue: this class also has open carries #107289, #104248, #100679 and #99712 (Matrix-only), and the fresh report #114134 — the Matrix |
kvnloo
left a comment
There was a problem hiding this comment.
The fix is correct. The shared media dispatcher (gateway/platforms/base.py:4058) calls self.send_voice(..., is_voice=is_voice), and these four overrides narrowed the base signature without **kwargs — so every TTS voice reply on Matrix, LINE, Mattermost, and Weixin raised TypeError before upload, silently dropped since the call site wraps in suppress(Exception). The four **kwargs additions restore the base contract exactly; the Matrix docstring addition documents the accept-and-ignore, matching the base adapter's own tolerance.
One gap: the new contract test doesn't cover Weixin. _VOICE_OVERRIDES lists only line/matrix/mattermost, but this PR also fixes WeixinAdapter.send_voice (and send_video). Suggest adding ("gateway.platforms.weixin", "WeixinAdapter") to the list so the fourth adapter gets the same regression guard.
Minor: the Weixin send_video hunk is defensive — the current dispatch (base.py:4062) doesn't pass extra kwargs to send_video, so nothing breaks either way. Fine to keep for contract symmetry.
Verdict: correct fix, one test-coverage nit above. Safe to merge once the Weixin entry is added (or as-is; the runtime fix itself is complete).
Summary
MEDIA:audio replies (TTS voice messages, audio deliverables) are silently dropped on Matrix, Mattermost, and LINE: the text arrives, but no voice bubble or file attachment is ever uploaded. Fixes #113951.Root cause
The shared media-delivery chain passes
is_voice=to every adapter'ssend_voice():gateway/platforms/base.py_send_one(~L3953)gateway/run_notifications.py(~L326)except— warning loggedgateway/run_turn.py(~L2406)suppress(Exception)— fully silentBasePlatformAdapter.send_voiceaccepts**kwargs, but three plugin adapters override it with a narrower signature and no kwarg tolerance, so the call raisesTypeError: send_voice() got an unexpected keyword argument 'is_voice'before any upload happens:plugins/platforms/matrix/adapter.pyplugins/platforms/mattermost/adapter.pyplugins/platforms/line/adapter.pyObserved on Matrix (gateway.log):
Fix
Accept-and-ignore
**kwargsin the threesend_voiceoverrides, matching the base adapter's signature tolerance. Voice-vs-file routing is decided by the caller (should_send_media_as_audio+ the[[audio_as_voice]]media tag); the adapters don't need the flag, they just must not reject it.Testing
tests/gateway/test_media_send_voice_kwargs.py: the three overrides must tolerate the dispatcher's exact call shape (is_voice=+metadata=), and the base signature binds the dispatcher's kwargs. Red on base (3 failed), green with this fix.test_matrix_voice.py,test_voice_transcode.py,test_tts_media_routing.py(16 passed, 1 skipped).Notes for reviewers
is_voiceflag already reaches adapters through other signatures (e.g. Matrix passes it internally to_send_local_file(..., is_voice=True)for the MSC3245 voice field); this PR only removes the signature mismatch at the override boundary.plugins/platforms/*/adapter.pyoverrides ofsend_voice/send_document/send_image_file/send_video; Matrix, Mattermost, and LINE were the only three affected.