fix(telegram): queue voice follow-ups instead of interrupting in-flight reply (#31328) - #31342
Conversation
…ht reply (#31328) When a user sends voice notes in rapid succession, voice 2 arriving mid-processing of voice 1 currently sets the per-session interrupt Event. The stale-response check in ``_process_message_background`` then suppresses voice 1's reply, and voice 1 is silently dropped from the session — no ``response ready`` line, no JSONL entry, no message to the user. Photo bursts already had a queue-don't-interrupt exception for exactly this UX shape; voice bursts have the same shape so they follow the same contract. Extends the existing PHOTO special case to also cover VOICE in both the adapter-level (``BasePlatformAdapter.handle_message``) and runner-level (``GatewayRunner._handle_message``) priority intercepts. After the fix, voice 1's reply is delivered, voices 2+ accumulate in ``_pending_messages`` (already merged via ``merge_pending_message_event`` since both have media_urls), and the existing pending-drain logic spawns a fresh turn for the merged batch when voice 1 completes. Test: tests/gateway/test_interrupt_key_match.py + tests/gateway/test_telegram_photo_interrupts.py — adds two regression tests mirroring the existing photo-followup ones. Both new tests fail without this change and pass with it; 74 surrounding gateway tests remain green. Platform: telegram Issue: #31328
|
Thanks for the cluster pointers @alt-glitch. Quick diff for a reviewer collapsing the three: vs #8434 — same direction (queue voice without interrupt), but #8434 only patches vs #19825 — different mechanism. #19825 keeps the interrupt and stores the full |
What
Extends the existing PHOTO queue-don't-interrupt special case in both
BasePlatformAdapter.handle_messageandGatewayRunner._handle_messageto also coverMessageType.VOICE.Why
When a user sends voice notes in rapid succession, voice 2 arriving mid-processing of voice 1 currently:
BasePlatformAdapter.handle_messageinterrupt_eventand queues into_pending_messages_process_message_backgroundtask finishes, generates a reply, then the stale-response check at base.py:3255-3265 —interrupt_event.is_set() and session_key in self._pending_messages— silently setsresponse = Noneresponse readylog, noSending response, no JSONL entry. The user sees no reply for voice 1, only the merged voices 2+3 batch turn arrives.This matches the symptom the reporter observed — voice 2 had a
conversation turn: history=6log but noresponse readyline and no session JSONL entry, while voices 3+4 went on to produce a normal batched response.Photo bursts already had this protection (added long ago for albums). Rapid voice notes have the same UX shape — the user is providing more inputs, not requesting an interrupt — so they follow the same contract.
After the fix:
_pending_messagesviamerge_pending_message_event, which already extendsmedia_urlsfor events that both have media — so multiple late-arriving voices land in a single merged event_process_message_backgroundspawns a fresh turn for the merged batch — exactly the behavior the reporter described as expected ("merge late-arriving voices" path)This is option (a) from the issue ("wait for in-flight turns to finish before starting a new batch") implemented as the same queue-don't-interrupt pattern as photo bursts, rather than inventing new mid-flight merge semantics.
Scope kept narrow: only
MessageType.VOICEis added (not AUDIO/VIDEO/STICKER/DOCUMENT) to match the reported issue precisely. The same shape likely applies to AUDIO/VIDEO and could be extended in a follow-up; happy to broaden in this PR if reviewers prefer.Test
tests/gateway/test_interrupt_key_match.py::test_voice_followup_is_queued_without_interrupt— adapter-level: VOICE event during active session must queue into_pending_messageswithout settinginterrupt_eventtests/gateway/test_telegram_photo_interrupts.py::test_handle_message_does_not_priority_interrupt_voice_followup— runner-level: VOICE event arriving with a registered running agent must queue without callingrunning_agent.interrupt()Both new tests are direct mirrors of the existing photo-followup tests, so they enforce the same contract for voice that has been working for photos.
Verification:
test_session_race_guard,test_active_session_text_merge,test_queue_consumption,test_telegram_text_batching,test_busy_session_ack,test_duplicate_reply_suppression)Platform
telegram (the path is shared by all platforms that route through
BasePlatformAdapter.handle_message, but the bug surfaces most clearly on Telegram where voice notes are common)Issue
Closes #31328