feat(channel): add WeChat typing indicator support#422
Open
lankerens wants to merge 1 commit into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(channel): add WeChat typing indicator support
中文
概述
为微信渠道添加"正在输入"指示器支持,对齐 Hermes(Python)实现。当收到微信消息且 Agent 开始处理时,用户端会显示"正在输入"指示器,直到 Agent 完成回复。
业务流程对比
改动前:
问题:用户发送消息后到收到回复之间没有任何视觉反馈,体验差。
改动后:
架构
变更文件(6 个文件,+206 -3)
types.rsGetConfigRequest、SendTypingRequest、TYPING_START/TYPING_STOP常量api.rsget_config()和send_typing()API 方法,含ret/errcode错误检查plugin.rsChannelPlugintrait 新增start_typing()/stop_typing()默认空方法weixin/plugin.rsmanager.rsChannelSender的 typing 方法,委托给 pluginstream_relay.rsrun_weixin中每 2 秒持续刷新 typing 指示器关键设计决策
Typing 生命周期 = Relay 生命周期
start_typing在 relay 启动时立即调用,stop_typing在send_message完成后调用,确保指示器覆盖整个处理周期。每 2 秒持续刷新
对齐 Hermes 的
_keep_typing模式,spawned task 每 2 秒调用一次start_typing,确保客户端重连后指示器恢复。先发消息再关 typing
顺序:
send_message → abort 刷新 → stop_typing,避免指示器消失和消息到达之间的空白期。传递 context_token
微信 API 的
getconfig接口需要ilink_user_id和context_token才能返回有效的typing_ticket。context_token从入站消息中提取并按会话缓存。sendtyping 使用 ilink_user_id
sendtyping请求必须包含ilink_user_id,API 才能识别哪个会话应显示指示器。API 错误检查
get_config()和send_typing()均检查响应中的ret/errcode,返回描述性错误而非静默忽略。日志级别
正常操作使用
debug!,失败使用warn!,避免 2 秒刷新周期产生日志噪音。API 详情
POST
ilink/bot/getconfig请求:
{ "ilink_user_id": "<user_id>@im.wechat", "context_token": "<context_token>" }响应:
{ "ret": 0, "typing_ticket": "<ticket>" }POST
ilink/bot/sendtyping请求:
{ "ilink_user_id": "<user_id>@im.wechat", "typing_ticket": "<ticket>", "status": 1 }status:1 = 开始输入,2 = 停止输入测试报告
单元测试
cargo test -p aionui-channel集成测试用例
回归测试
与 Hermes 对齐
{"ilink_user_id": ..., "context_token": ...}{"ilink_user_id": ..., "typing_ticket": ..., "status": ...}English
Summary
Add "typing" indicator support for WeChat channel, aligning with Hermes (Python) implementation. When a WeChat message is received and the agent starts processing, the user sees a "typing..." indicator until the agent finishes responding.
Business Flow Comparison
Before:
Problem: No visual feedback between user sending a message and receiving a reply, resulting in poor experience.
After:
Architecture
Files Changed (6 files, +206 -3)
types.rsGetConfigRequest,SendTypingRequest,TYPING_START/TYPING_STOPconstantsapi.rsget_config()andsend_typing()API methods withret/errcodecheckingplugin.rsstart_typing()/stop_typing()default no-op methods toChannelPlugintraitweixin/plugin.rsmanager.rsChannelSendertyping methods, delegate to pluginstream_relay.rsrun_weixinKey Design Decisions
Typing lifecycle = relay lifecycle
start_typingis called immediately when relay starts,stop_typingis called aftersend_messagecompletes. This ensures the indicator covers the entire processing period including agent thinking time.Continuous refresh every 2s
A spawned task calls
start_typingevery 2 seconds, matching Hermes_keep_typingpattern. Ensures the indicator persists across WeChat client reconnections.Message sent before typing stopped
Order:
send_message → abort refresh → stop_typing, avoiding a gap between indicator disappearing and message appearing.context_tokenpassed togetconfigThe WeChat API requires
ilink_user_idandcontext_tokenin thegetconfigrequest to return a validtyping_ticket. Thecontext_tokenis extracted from inbound messages and cached per chat.ilink_user_idinsendtypingThe
sendtypingrequest must includeilink_user_idfor the API to identify which session should show the indicator.API error checking
Both
get_config()andsend_typing()checkret/errcodein the response and return descriptive errors instead of silently ignoring failures.Log levels
Normal typing operations use
debug!, failures usewarn!. Avoids log noise from the 2-second refresh cycle while keeping errors visible.API Details
POST
ilink/bot/getconfigRequest:
{ "ilink_user_id": "<user_id>@im.wechat", "context_token": "<context_token>" }Response:
{ "ret": 0, "typing_ticket": "<ticket>" }POST
ilink/bot/sendtypingRequest:
{ "ilink_user_id": "<user_id>@im.wechat", "typing_ticket": "<ticket>", "status": 1 }status: 1 = start typing, 2 = stop typingTest Report
Unit Tests
cargo test -p aionui-channelIntegration Test Cases
getconfigAPI returns errorwarn!log emitted, typing silently skipped, message processing unaffectedsendtypingAPI returns error (expired ticket)warn!log emitted, next attempt re-fetcheswarn!log emitted, no crashRegression Tests
start_typing/stop_typingare default no-opHermes Alignment
getconfigrequest body{"ilink_user_id": ..., "context_token": ...}sendtypingrequest body{"ilink_user_id": ..., "typing_ticket": ..., "status": ...}send_message, thenstop_typingwarn!log, clear cache, continue