mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix(telegram): route resumed DM topic sends directly
This commit is contained in:
parent
2994bf494d
commit
de4cb55bf3
3 changed files with 42 additions and 14 deletions
|
|
@ -45,10 +45,10 @@ def _thread_metadata_for_source(source, reply_to_message_id: str | None = None)
|
|||
|
||||
Most platforms route threaded sends with a generic ``thread_id`` metadata
|
||||
value. Telegram private-chat topics created through Hermes' DM-topic helper
|
||||
are exposed in updates as ``message_thread_id`` plus a reply anchor, but
|
||||
outbound sends only render in the correct Telegram lane when the adapter
|
||||
supplies both ``message_thread_id`` and ``reply_to_message_id``. Mark those
|
||||
lanes so the Telegram adapter can avoid the known-bad partial routes.
|
||||
are exposed in updates as ``message_thread_id`` plus a reply anchor. Live
|
||||
user-message replies route with ``message_thread_id`` + ``reply_to_message_id``;
|
||||
synthetic/resumed sends that have no reply anchor fall back to Telegram's
|
||||
``direct_messages_topic_id`` when the Bot API supports it.
|
||||
"""
|
||||
thread_id = getattr(source, "thread_id", None)
|
||||
if thread_id is None:
|
||||
|
|
@ -56,6 +56,9 @@ def _thread_metadata_for_source(source, reply_to_message_id: str | None = None)
|
|||
metadata = {"thread_id": thread_id}
|
||||
if _platform_name(getattr(source, "platform", None)) == "telegram" and getattr(source, "chat_type", None) == "dm":
|
||||
metadata["telegram_dm_topic_reply_fallback"] = True
|
||||
tid = str(thread_id)
|
||||
if tid and tid not in {"", "1"}:
|
||||
metadata["direct_messages_topic_id"] = tid
|
||||
anchor = reply_to_message_id or getattr(source, "message_id", None)
|
||||
if anchor is not None:
|
||||
metadata["telegram_reply_to_message_id"] = str(anchor)
|
||||
|
|
@ -67,10 +70,9 @@ def _reply_anchor_for_event(event) -> str | None:
|
|||
|
||||
Telegram forum/supergroup topics should be routed by topic metadata, not by
|
||||
replying to the triggering message. Hermes-created Telegram private-chat
|
||||
topic lanes are different: Bot API sends reject their ``message_thread_id``
|
||||
and do not route with ``direct_messages_topic_id``. Those lanes only remain
|
||||
visible when sent with both the private topic thread id and a reply to the
|
||||
triggering user message.
|
||||
topic lanes prefer replying to the triggering user message so the answer
|
||||
stays attached to the active lane; synthetic/resumed sends fall back to
|
||||
``direct_messages_topic_id`` metadata when no message id is available.
|
||||
"""
|
||||
source = getattr(event, "source", None)
|
||||
platform = _platform_name(getattr(source, "platform", None))
|
||||
|
|
|
|||
|
|
@ -560,9 +560,10 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||
Supergroup/forum topics use ``message_thread_id``. True Bot API Direct
|
||||
Messages topics can opt in with explicit ``direct_messages_topic_id``
|
||||
metadata. Hermes-created private-chat topic lanes are marked with
|
||||
``telegram_dm_topic_reply_fallback`` and must send the private topic
|
||||
thread id together with a reply anchor. Live testing showed that either
|
||||
parameter alone can render outside the visible lane.
|
||||
``telegram_dm_topic_reply_fallback``. Live replies send the private
|
||||
topic thread id together with a reply anchor; synthetic/resumed sends
|
||||
without an anchor use ``direct_messages_topic_id`` when metadata has it.
|
||||
``message_thread_id`` alone can render outside the visible lane.
|
||||
|
||||
When ``reply_to_mode`` is ``"off"``, the reply anchor is suppressed for
|
||||
DM topic fallback sends while preserving the ``message_thread_id`` so
|
||||
|
|
@ -574,6 +575,12 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||
if reply_to_message_id is None:
|
||||
reply_to_message_id = cls._metadata_reply_to_message_id(metadata)
|
||||
if reply_to_message_id is None:
|
||||
direct_topic_id = cls._metadata_direct_messages_topic_id(metadata)
|
||||
if direct_topic_id is not None:
|
||||
return {
|
||||
"message_thread_id": None,
|
||||
"direct_messages_topic_id": int(direct_topic_id),
|
||||
}
|
||||
return {}
|
||||
return {"message_thread_id": cls._message_thread_id_for_send(thread_id)}
|
||||
direct_topic_id = cls._metadata_direct_messages_topic_id(metadata)
|
||||
|
|
|
|||
|
|
@ -331,10 +331,28 @@ def test_base_gateway_metadata_marks_telegram_dm_topics_as_reply_fallback():
|
|||
assert metadata == {
|
||||
"thread_id": "20189",
|
||||
"telegram_dm_topic_reply_fallback": True,
|
||||
"direct_messages_topic_id": "20189",
|
||||
"telegram_reply_to_message_id": "462",
|
||||
}
|
||||
|
||||
|
||||
def test_base_gateway_metadata_for_resumed_telegram_dm_topic_uses_direct_topic():
|
||||
"""Resumed/synthetic DM-topic events may have no reply anchor."""
|
||||
source = SimpleNamespace(
|
||||
platform=Platform.TELEGRAM,
|
||||
chat_type="dm",
|
||||
thread_id="20189",
|
||||
)
|
||||
|
||||
metadata = _thread_metadata_for_source(source)
|
||||
|
||||
assert metadata == {
|
||||
"thread_id": "20189",
|
||||
"telegram_dm_topic_reply_fallback": True,
|
||||
"direct_messages_topic_id": "20189",
|
||||
}
|
||||
|
||||
|
||||
def test_base_gateway_replies_to_triggering_message_for_telegram_dm_topic():
|
||||
"""Private DM topic lanes should anchor replies to the active user message."""
|
||||
event = SimpleNamespace(
|
||||
|
|
@ -533,7 +551,7 @@ async def test_send_model_picker_uses_metadata_reply_fallback_for_dm_topics():
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_dm_topic_fallback_without_anchor_does_not_crash():
|
||||
"""DM-topic fallback without an anchor must not use message_thread_id alone."""
|
||||
"""DM-topic fallback without an anchor uses direct topic routing."""
|
||||
adapter = _make_adapter()
|
||||
call_log = []
|
||||
|
||||
|
|
@ -549,13 +567,14 @@ async def test_send_dm_topic_fallback_without_anchor_does_not_crash():
|
|||
metadata={
|
||||
"thread_id": "20197",
|
||||
"telegram_dm_topic_reply_fallback": True,
|
||||
"direct_messages_topic_id": "20197",
|
||||
},
|
||||
)
|
||||
|
||||
assert result.success is True
|
||||
assert call_log[0]["reply_to_message_id"] is None
|
||||
assert "message_thread_id" not in call_log[0]
|
||||
assert "direct_messages_topic_id" not in call_log[0]
|
||||
assert call_log[0]["message_thread_id"] is None
|
||||
assert call_log[0]["direct_messages_topic_id"] == 20197
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue