添加 /stop 抢占功能以立即中止当前消息、清空排队消息 - #614
Open
JingWwwwwj wants to merge 2 commits into
Open
Conversation
当用户发送 /stop 时,不再将其排在待处理消息之后等待执行,而是: 增加每个会话的生代际计数器(generation counter),跳过已排队但尚未执行的消息,同时终止当前正在执行的消息。 直接调用处理程序,触发 openclaw SDK 的中止路径 修复了 /stop 命令必须等待前面 N 个排队消息处理完毕才能生效的用户体验问题。
Collaborator
|
谢谢 PR,设计思路讲得很清楚。Review 下来有几个问题需要处理: P0 必修
JSDoc 和 PR 说明都强调:「必须是 OpenClaw 请删掉 P1 必补本 PR 没有任何测试 这块涉及 Promise 链 + 代际计数 + 抢跑并发,逻辑细微,必须有单测覆盖以下场景后再合:
P2 建议另开 issue 跟进(不阻塞本 PR)
P0 + P1 处理完后可以再丢上来。一天内合入 |
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.
钉钉插件/stop 抢跑 —— 代码改动说明
一、问题回顾
用户通过钉钉给 OpenClaw 发了一条耗时很长的消息
msg1,agent 正在生成;后续msg2…msgN已经排在本地串行队列里。此时用户想终止,发了/stop。期望的行为:
/stop立即抢占当前 run,触发 abort,并清空所有待处理消息。修改前的实际行为:
/stop被当成普通消息塞进同一条 promise 串行链尾部,必须等msg1…msgN全部处理完才轮到自己执行;而那时msg1早已自然结束,abort时已无事可做 —— stop 等于没按。二、改动总览
只修改了 dingtalk 插件源码中的一个文件:
isStopCommand()函数/stop或stopsessionGenerations代际数handleDingTalkMessage入口)三、详细改动
改动 1:新增
isStopCommand()函数位置:
message-handler.ts:107-116为什么这么写:
/stop和stop,这两个是 openclaw 官方 SDKisAbortRequestText的真子集,绝对安全。效果:拿到一条文本立即就能判断"要不要抢跑",没什么副作用。
改动 2:新增
sessionGenerations代际计数表位置:
message-handler.ts:98-105为什么这么写:
JavaScript 的 promise 链有一个关键性质:Map 里的 promise 引用只是"队列尾巴的句柄"。
.delete()只是丢掉句柄,并不会取消已经挂在 promise 链上的**.then**回调。msg2…msgN 还会被原链按顺序执行完。要真正"清空待处理队列",必须在每个
.then回调里加一个"我还该不该执行"的检查:taskGeneration = sessionGenerations.get(queueKey)currentGen,不一致就跳过该消息直接不处理/stop来时把sessionGenerations[queueKey] +1,所有"过期代际"的待执行任务自动被废掉效果:把"清队列"变成了"每条消息出队前自检",但实现效果上完全等价于队列被清空。
改动 3:入口加
/stop检查位置:
message-handler.ts:1724-1752算完
queueKey之后、原入队代码之前:设计决策说明:
await handleDingTalkMessageInternal(...)sessionQueues.delete().then,必须靠代际检查一下preCreatedCard: undefined+emotionAlreadyAdded: falsetry / catch / return效果:
改动 4:入队任务之前加代际检查
位置:
message-handler.ts:1815-1826为什么这么写:
taskGeneration在入队那一刻就确定/stop(代际 +1),就跳过handleDingTalkMessageInternal,promise 链本身仍然推进,后续消息照常出队效果:和改动 3 的"代际数增加"配合,才让 stop 具有"清空已排队消息"的能力。
改动 5:TTL 清理同步带上代际表
位置:
message-handler.ts:118-127为什么需要:新增了
sessionGenerations这个 Map,需要挂到现有 5 分钟 TTL 清理逻辑里。四、修改前后行为对比
任务场景:同一时刻有
msg1在跑、msg2-msg5在排队,用户发/stop修改前
/stop排在最后,必须等前面 5 条全跑完msg1早已结束,abort 无意义修改后
/stop抢跑,与msg1在 event loop 里并发tryFastAbortFromMessage触发msg1的 AbortSignal,run 立即中断msg2..msg5在轮到自己时检测到代际过期,自动跳过普通消息路径不变(仍走串行队列),仅多了一道"代际是否过期"的轻量检查。
五、未改动的部分