Skip to content

feat(gateway): inbound message debounce to merge rapid messages into one turn - #2484

Closed
teyrebaz33 wants to merge 2 commits into
NousResearch:mainfrom
teyrebaz33:feat/gateway-inbound-debounce
Closed

feat(gateway): inbound message debounce to merge rapid messages into one turn#2484
teyrebaz33 wants to merge 2 commits into
NousResearch:mainfrom
teyrebaz33:feat/gateway-inbound-debounce

Conversation

@teyrebaz33

Copy link
Copy Markdown
Contributor

Closes #2434

Problem

Discord auto-splits messages >2000 chars into multiple rapid-fire updates. Each fragment triggers a separate agent turn — causing fragmented responses, wasted API calls, and lost context.

Solution

Configurable inbound_debounce_ms that buffers rapid consecutive text messages from the same sender and merges them into a single turn after the window expires.

# config.yaml
inbound_debounce_ms: 3000  # 3 seconds

Implementation

  • GatewayConfig.inbound_debounce_ms (default 0 = disabled, backward compatible)
  • GatewayRunner._debounce_tasks / _debounce_buffers — per session key
  • _flush_debounce_buffer() — merges event texts with newline, dispatches once
  • Each new message cancels the existing timer and restarts the window
  • Only TEXT messages are debounced; photo/voice pass through immediately
  • Only applies when no agent is running — priority interrupt path takes precedence
  • Debounce tasks are cancelled cleanly on /new, /reset, and gateway shutdown

Not included (V2)

Per-platform config (discord.debounce_ms, telegram.debounce_ms) — global setting covers the main use case.

…one turn

Adds configurable inbound_debounce_ms to GatewayConfig. When set, rapid
consecutive text messages from the same sender are buffered and merged
into a single agent turn after the debounce window expires.

Fixes Discord auto-split messages and rapid-fire corrections being
processed as separate agent turns — reducing API costs and improving
context coherence.

Implementation:
- GatewayConfig.inbound_debounce_ms (default 0 = disabled)
- Loaded from config.yaml inbound_debounce_ms key
- GatewayRunner._debounce_tasks / _debounce_buffers per session
- _flush_debounce_buffer(): merges buffered events, dispatches once
- Only applies to TEXT messages; photo/voice pass through immediately
- Only when no agent is running (priority interrupt path takes precedence)
- Debounce tasks cancelled on /new, /reset, and gateway shutdown

Usage:
  inbound_debounce_ms: 3000  # in config.yaml

Closes NousResearch#2434
@simplenamebox-ops

Copy link
Copy Markdown
Contributor

I tested this PR locally and can confirm it solves a real issue I encountered.

Problem: When sending multiple messages in quick succession (before the agent finishes responding to the first), the gateway queues the second message and signals an interrupt to the running agent. However, the agent pivots to the new message without completing the first response. The first question is abandoned mid-task.

Example flow:

  1. User sends "Check issue Memory flush agent overwrites live memory on session reset/gateway restart #2670"
  2. Agent starts browsing GitHub, navigating to the issue
  3. User sends "Also check feat(config): support ${ENV_VAR} substitution in config.yaml #2684 while you're there" (0.3s later)
  4. Agent receives interrupt signal, pivots to feat(config): support ${ENV_VAR} substitution in config.yaml #2684
  5. Agent responds to feat(config): support ${ENV_VAR} substitution in config.yaml #2684, never finishes Memory flush agent overwrites live memory on session reset/gateway restart #2670

What this PR does: Buffers rapid messages and merges them into a single turn:

User sends: "Check issue #2670"
User sends: "Also check #2684" (0.3s later)
Gateway waits for debounce window (e.g., 3s)
Gateway merges: "Check issue #2670\n\nAlso check #2684"
Agent responds to both in single turn

Testing: I ran the test suite locally — all 10 tests pass:

tests/gateway/test_debounce.py::TestDebounceConfig - 5 passed
tests/gateway/test_debounce.py::TestDebounceBuffering - 5 passed
============================== 10 passed in 2.09s

Issue with PR: The branch appears to be based on an outdated main. Git diff shows -4,507 lines including deleted test files that exist in current main. Rebase needed:

git fetch origin main
git rebase origin/main
git push --force-with-lease

This feature is valuable for multi-message workflows on messaging platforms. The implementation is clean — config-driven, backward compatible (disabled by default), and limited to text messages (photos/voice pass through immediately).
EOF; __hermes_rc=$?; printf 'HERMES_FENCE_a9f7b3'; exit $__hermes_rc

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the patch @teyrebaz33 — closing as superseded. The same capability already landed on main as per-platform text batching (better fit than a gateway-level global debounce):

  • Discord (gateway/platforms/discord.py): HERMES_DISCORD_TEXT_BATCH_DELAY_SECONDS (default 0.6s) + HERMES_DISCORD_TEXT_BATCH_SPLIT_DELAY_SECONDS (default 2.0s). Merges rapid successive text messages (including Discord's client-side split of messages >2000 chars) into a single MessageEvent before dispatching.
  • Telegram (gateway/platforms/telegram.py): mirror env vars HERMES_TELEGRAM_TEXT_BATCH_DELAY_SECONDS / HERMES_TELEGRAM_TEXT_BATCH_SPLIT_DELAY_SECONDS.
  • Base adapter (gateway/platforms/base.py): merge_pending_message_event(..., merge_text=True) also merges text fragments that arrive while an agent turn is already running, so follow-ups aren't silently truncated to the last chunk.

Key differences from this PR:

  1. Per-platform knobs instead of one global setting — Discord and Telegram can tune independently.
  2. Adaptive longer delay when the last chunk is near the client's split boundary (2000 / 4096 chars), which is the exact auto-split case from [Feature] Inbound message debounce for gateway (Discord/Telegram) #2434.
  3. Runs at the adapter layer so platforms that don't need it (API, CLI-style) pay zero overhead.

Closing #2434 in parallel. Appreciate the clean implementation — apologies we didn't catch this overlap sooner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Inbound message debounce for gateway (Discord/Telegram)

3 participants