Skip to content

fix(hermes-channel-dmwork): DM reply support, MessageType tolerance, reply payload crash#165

Open
Jerry-Xin wants to merge 3 commits intodmwork-org:mainfrom
Jerry-Xin:fix/hermes-channel-dmwork-dm-support
Open

fix(hermes-channel-dmwork): DM reply support, MessageType tolerance, reply payload crash#165
Jerry-Xin wants to merge 3 commits intodmwork-org:mainfrom
Jerry-Xin:fix/hermes-channel-dmwork-dm-support

Conversation

@Jerry-Xin
Copy link
Copy Markdown
Collaborator

Summary

Three fixes for hermes-channel-dmwork discovered during Hermes Agent deployment with DMWork:

Fix 1: DM messages fail to send (400 'bot is not a member of this group')

Root cause: _handle_recv() only populates event.raw_message with channel_type for group messages. For DMs, raw_message is empty, so send() defaults to ChannelType.Group → API rejects with 400.

Fix: Always set event.raw_message with channel_type from the RECV packet, regardless of message type.

Fix 2: WebSocket reconnection storm on unknown MessageType (e.g. type=99)

Root cause: MessagePayload.from_dict() calls MessageType(data.get('type', 1)). When the server sends a system message with type=99 (not in the IntEnum), it raises ValueError, which triggers WebSocket disconnect and reconnect.

Fix: Added _safe_message_type() helper that catches ValueError and falls back to the raw int value.

Fix 3: Reply payload format crashes DMWork web client

Root cause: api.py sends {"reply": {"message_id": "xxx"}} in the message payload. The DMWork web client expects reply.payload to be a string and calls .startsWith() on it. Since payload is undefined in this format, the entire chat view crashes with TypeError: Cannot read properties of undefined (reading 'startsWith').

Fix: Disabled reply field injection until the correct format is determined. This matches the behavior of hermes-octo-adapter, which also does not send the reply field.

Files Changed

  • hermes-channel-dmwork/src/hermes_dmwork/adapter.py — Always populate event.raw_message with channel_type
  • hermes-channel-dmwork/src/hermes_dmwork/types.py — Add _safe_message_type() fallback
  • hermes-channel-dmwork/src/hermes_dmwork/api.py — Disable reply field (with TODO)

Testing

Tested on a live Hermes Agent deployment connected to DMWork (im.deepminer.com.cn):

  • ✅ DM messages now send successfully with correct channel_type
  • ✅ No more WebSocket reconnection on type=99 system messages
  • ✅ Web client no longer crashes when loading bot conversations

忻役 added 3 commits April 11, 2026 18:13
…reply payload crash

Three fixes for hermes-channel-dmwork:

1. **DM channel_type passthrough**: Always populate event.raw_message
   with channel_type from the RECV packet, not just for group messages.
   Previously, DM messages had no channel_type in metadata, causing
   send() to default to ChannelType.Group and fail with 400
   'bot is not a member of this group'.

2. **MessageType tolerance**: Unknown message type values (e.g. type=99
   for system messages) caused ValueError in MessagePayload.from_dict()
   because IntEnum rejects unknown values. Added _safe_message_type()
   helper that falls back to raw int for unrecognized types, preventing
   WebSocket reconnection storms.

3. **Reply payload crash**: The reply field format
   {"message_id": "xxx"} lacks the payload property that the DMWork
   web client expects, causing 'Cannot read properties of undefined
   (reading startsWith)' and crashing the entire chat view. Disabled
   reply field injection until the correct format is determined.
…messages

The require_mention config option was read but never enforced.
All group messages were dispatched to the LLM regardless of whether
the bot was @mentioned. This caused the bot to respond to every
message in group chats.

Added mention filtering: when require_mention is true (default),
group messages that don't @mention the bot's robot_id are silently
dropped before reaching the LLM.
The send() method previously passed @[uid:name] as raw text without
building mention entities for the DMWork API. This meant the bot
could not @mention other users in group chats.

Added:
- mention.py: parse_structured_mentions() and convert_structured_mentions()
  to parse @[uid:name] from LLM output and convert to @name display
  format with proper mention entities
- adapter.py: process outbound mentions in send() before dispatching
  to the API, passing mention_uids and mention_entities
Copy link
Copy Markdown
Collaborator

@lml2468 lml2468 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: 可合,但有两个前置确认项

整体方向正确:DM 回复支持、_safe_message_type 容错、parse_structured_mentions 解析,逻辑清晰。

需要确认再合入:

  1. 目标分支是 main 而非 develop — 是有意的吗?如果是 hotfix 请说明,否则建议改到 develop

  2. Reply payload 被注释掉(前端 crash:缺少 payload 字段) — 这是临时止血可以理解,但合入后必须立刻开一个 issue 跟进,不能就这样烂在 main 里。请在合入前创建对应 issue 并在 PR description 里 link。

确认以上两点后即可合入。

Copy link
Copy Markdown
Collaborator

@lml2468 lml2468 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: 可合,两个问题需要跟进

整体方向正确,三处改动都有价值:

1. require_mention 过滤(adapter.py:707)
逻辑正确,在处理 GROUP.md 事件之前过滤,不会误截断事件流。

2. raw_message 重构(adapter.py:838)
channel_type 提升到公共赋值,DM 和 Group 消息都会携带,改善了下游判断。比之前只在 Group 时赋值更干净。

3. parse_structured_mentions / convert_structured_mentions(mention.py:237+)
实现正确:offset 计算按替换后的位置累积,valid_uids 过滤到位,__slots__ 做了性能优化。


⚠️ 问题一:reply payload 永久注释(api.py:266)

# if reply_msg_id:
#     payload["reply"] = {"message_id": reply_msg_id}

注释掉是合理的临时止血,但合入后必须有跟踪。请在合入前开一个 issue(建议标题:fix: reply payload format causes frontend crash),并在 PR description 里 link,否则这个 TODO 会永远烂在代码里。

⚠️ 问题二:目标分支是 main 而非 develop

其他 PR(#152/#169/#177/#173)均指向 develop。如果这是有意的 hotfix 请说明;如果不是,建议改成 develop 走正常发布流程。

⚠️ 问题三:无测试覆盖

Python adapter 改动涉及 require_mention 过滤和 outbound mention 转换,均无测试。至少需要为 parse_structured_mentionsconvert_structured_mentions 补单元测试。


请确认以上三点后可合入。

Copy link
Copy Markdown
Collaborator

@lml2468 lml2468 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

步骤 1 代码审查:✅ PASS

三个 bug 修复都有清晰的根因分析,改法正确:

  1. DM channel_type 丢失event.raw_message 从仅 group 填充改为始终填充——修复 DM 回复 400 错误
  2. MessageType ValueError 风暴_safe_message_type() fallback 到 raw int——防止 type=99 等未知类型触发 WS 重连
  3. Reply payload 前端崩溃:暂时禁用 reply 字段注入,留 TODO——务实选择

额外加分:

  • 出站 @[uid:name] 结构化 mention 转换逻辑完整(parse → convert → entities)
  • 群消息 _require_mention 过滤器

代码风格小 note:import re as _re 放在文件中段而非顶部,Python 习惯上 import 集中在文件开头,但不影响功能。

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.

2 participants