mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-08 08:11:38 +00:00
fix(telegram): respect reply_to_mode for DM topic reply fallback
The DM topic reply fallback code in send() hardcoded should_thread=True when telegram_dm_topic_reply_fallback metadata was present, bypassing _should_thread_reply() and ignoring reply_to_mode config. This caused quote bubbles on every response even with reply_to_mode: 'off'. Fix: - Add reply_to_mode param to _reply_to_message_id_for_send() and _thread_kwargs_for_send() classmethods - In send(), check self._reply_to_mode != 'off' for DM topic fallback - Suppress reply anchor and reply_to_message_id when mode is 'off' while preserving message_thread_id for correct topic routing - Thread reply_to_mode through all 29 call sites Regression coverage: 10 new tests in test_telegram_reply_mode.py covering classmethod behavior, send() integration, and backward compatibility. Fixes reply_to_mode: 'off' ignored by Telegram DM topic reply fallback code #23994
This commit is contained in:
parent
7fad501f08
commit
21a15b6711
2 changed files with 150 additions and 13 deletions
|
|
@ -304,3 +304,110 @@ class TestTelegramYamlConfigLoading:
|
|||
load_gateway_config()
|
||||
|
||||
assert os.environ.get("TELEGRAM_REPLY_TO_MODE") == "all"
|
||||
|
||||
|
||||
class TestDMTopicFallbackReplyToMode:
|
||||
"""Tests for reply_to_mode enforcement on DM topic fallback paths.
|
||||
|
||||
Regression tests for https://github.com/NousResearch/hermes-agent/issues/23994:
|
||||
reply_to_mode 'off' was ignored when sending via Hermes-created DM topic
|
||||
lanes (telegram_dm_topic_reply_fallback metadata), causing quote bubbles
|
||||
despite the user setting reply_to_mode: 'off'.
|
||||
"""
|
||||
|
||||
DM_TOPIC_METADATA = {
|
||||
"thread_id": "42",
|
||||
"telegram_dm_topic_reply_fallback": True,
|
||||
"telegram_reply_to_message_id": "12345",
|
||||
}
|
||||
|
||||
# -- _reply_to_message_id_for_send classmethod --
|
||||
|
||||
def test_reply_to_id_suppressed_when_off(self):
|
||||
"""reply_to_mode='off' suppresses reply anchor for DM topic fallback."""
|
||||
result = TelegramAdapter._reply_to_message_id_for_send(
|
||||
None, self.DM_TOPIC_METADATA, reply_to_mode="off",
|
||||
)
|
||||
assert result is None
|
||||
|
||||
def test_reply_to_id_returned_when_first(self):
|
||||
"""reply_to_mode='first' still returns reply anchor for DM topic fallback."""
|
||||
result = TelegramAdapter._reply_to_message_id_for_send(
|
||||
None, self.DM_TOPIC_METADATA, reply_to_mode="first",
|
||||
)
|
||||
assert result == 12345
|
||||
|
||||
def test_reply_to_id_returned_when_all(self):
|
||||
"""reply_to_mode='all' still returns reply anchor for DM topic fallback."""
|
||||
result = TelegramAdapter._reply_to_message_id_for_send(
|
||||
None, self.DM_TOPIC_METADATA, reply_to_mode="all",
|
||||
)
|
||||
assert result == 12345
|
||||
|
||||
def test_reply_to_id_returned_when_no_mode(self):
|
||||
"""Without reply_to_mode, behavior is unchanged (backward compat)."""
|
||||
result = TelegramAdapter._reply_to_message_id_for_send(
|
||||
None, self.DM_TOPIC_METADATA,
|
||||
)
|
||||
assert result == 12345
|
||||
|
||||
def test_explicit_reply_to_overrides_mode(self):
|
||||
"""Explicit reply_to param always wins, regardless of mode."""
|
||||
result = TelegramAdapter._reply_to_message_id_for_send(
|
||||
"999", self.DM_TOPIC_METADATA, reply_to_mode="off",
|
||||
)
|
||||
assert result == 999
|
||||
|
||||
# -- _thread_kwargs_for_send classmethod --
|
||||
|
||||
def test_thread_kwargs_suppressed_reply_anchor_when_off(self):
|
||||
"""reply_to_mode='off' returns thread_id without reply anchor."""
|
||||
result = TelegramAdapter._thread_kwargs_for_send(
|
||||
"100", "42", self.DM_TOPIC_METADATA,
|
||||
reply_to_message_id=None, reply_to_mode="off",
|
||||
)
|
||||
assert result == {"message_thread_id": 42}
|
||||
|
||||
def test_thread_kwargs_returns_full_when_first(self):
|
||||
"""reply_to_mode='first' returns thread_id (reply anchor in send kwargs)."""
|
||||
result = TelegramAdapter._thread_kwargs_for_send(
|
||||
"100", "42", self.DM_TOPIC_METADATA,
|
||||
reply_to_message_id=12345, reply_to_mode="first",
|
||||
)
|
||||
assert result == {"message_thread_id": 42}
|
||||
|
||||
def test_thread_kwargs_no_mode_backward_compat(self):
|
||||
"""Without reply_to_mode, behavior is unchanged."""
|
||||
result = TelegramAdapter._thread_kwargs_for_send(
|
||||
"100", "42", self.DM_TOPIC_METADATA,
|
||||
reply_to_message_id=12345,
|
||||
)
|
||||
assert result == {"message_thread_id": 42}
|
||||
|
||||
# -- send() integration test --
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_dm_topic_off_no_quote(self, adapter_factory):
|
||||
"""send() with DM topic fallback and reply_to_mode='off' skips reply."""
|
||||
adapter = adapter_factory(reply_to_mode="off")
|
||||
adapter._bot = MagicMock()
|
||||
adapter._bot.send_message = AsyncMock(return_value=MagicMock(message_id=1))
|
||||
adapter.truncate_message = lambda content, max_len, **kw: ["chunk1"]
|
||||
|
||||
await adapter.send("12345", "test content", metadata=self.DM_TOPIC_METADATA)
|
||||
|
||||
call = adapter._bot.send_message.call_args_list[0]
|
||||
assert call.kwargs.get("reply_to_message_id") is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_dm_topic_first_still_quotes(self, adapter_factory):
|
||||
"""send() with DM topic fallback and reply_to_mode='first' still quotes."""
|
||||
adapter = adapter_factory(reply_to_mode="first")
|
||||
adapter._bot = MagicMock()
|
||||
adapter._bot.send_message = AsyncMock(return_value=MagicMock(message_id=1))
|
||||
adapter.truncate_message = lambda content, max_len, **kw: ["chunk1"]
|
||||
|
||||
await adapter.send("12345", "test content", metadata=self.DM_TOPIC_METADATA)
|
||||
|
||||
call = adapter._bot.send_message.call_args_list[0]
|
||||
assert call.kwargs.get("reply_to_message_id") == 12345
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue