mirror of
https://gitcode.com/JianFeeeee/homeagent-sdk.git
synced 2026-09-20 08:58:03 +00:00
feat(qq): 同一会话连续消息合并为一次中断 + 示例 SDK 指回仓库源码
需求(jianf):同一个人连发的数条消息应打包成一次中断,别逐条唤醒 Agent。 - debounce 合并:同一会话 + 同一发送者(群聊按 群号+QQ、私聊按 QQ)在 batch_window_ms(默认 1500)内的连续消息合成一批,每来一条重置计时; 整批不超过 batch_max_ms(默认 30000),避免对方持续刷屏时一直不投。 - n>1 时中断说明「短时间连续发来 N 条」并列出 message_id,建议一次 get_history 拿全上下文;n==1 沿用原文,行为与合并前逐字一致。 - 可配置 batch_window_ms / batch_max_ms,0 = 关闭合并(逐条投递)。 - Stop 时 flush 未到点批次,别把对方消息吞掉。 - 新增 4 条测试(同发送者合并 / 不同发送者不合并 / 窗口 0 逐条 / 单条沿用原文)。 顺带:deepsearch / vikunja 的 go.mod 与 plg.json 此前指向本地安装的 SDK 1.2.0, 导致无法用当前 SDK 重编(缺 InjectOptions.Priority)。改回 ../../ 仓库源码, 与其余示例一致。
This commit is contained in:
@ -7,6 +7,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitcode.com/JianFeeeee/homeagent-sdk/sdk"
|
||||
)
|
||||
@ -235,3 +236,79 @@ func TestDowngradedAuthStillAllowsQQOutput(t *testing.T) {
|
||||
t.Fatalf("读取类工具在降权时应被当前会话限制挡住: %#v", ctx2.Response)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 消息合并(debounce)----
|
||||
|
||||
// collectInterrupts 用注入钩子收集中断文本(避免测试依赖真实 SDK)。
|
||||
func collectInterrupts(p *Plugin) *[]string {
|
||||
got := []string{}
|
||||
p.injectHook = func(s string) { got = append(got, s) }
|
||||
return &got
|
||||
}
|
||||
|
||||
func TestConsecutiveMessagesFromSameSenderAreBatched(t *testing.T) {
|
||||
p := newPermissionTestPlugin(t)
|
||||
got := collectInterrupts(p)
|
||||
p.batchWindow = 20 * time.Millisecond
|
||||
p.batchMax = time.Second
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
p.enqueueInterrupt("private", 10001, 0, int64(100+i), "小明", "单条", false, false)
|
||||
}
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
|
||||
if len(*got) != 1 {
|
||||
t.Fatalf("同一发送者连发 3 条应合并成 1 次中断,实际 %d 次: %#v", len(*got), *got)
|
||||
}
|
||||
if !strings.Contains((*got)[0], "3 条消息") {
|
||||
t.Fatalf("合并中断应说明一共几条,实际: %s", (*got)[0])
|
||||
}
|
||||
// 三个 message_id 都要带上,模型才能取全
|
||||
for _, id := range []string{"100", "101", "102"} {
|
||||
if !strings.Contains((*got)[0], id) {
|
||||
t.Fatalf("合并中断漏了 message_id=%s: %s", id, (*got)[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifferentSendersAreNotBatchedTogether(t *testing.T) {
|
||||
p := newPermissionTestPlugin(t)
|
||||
got := collectInterrupts(p)
|
||||
p.batchWindow = 20 * time.Millisecond
|
||||
p.batchMax = time.Second
|
||||
|
||||
p.enqueueInterrupt("private", 10001, 0, 1, "小明", "a", false, false)
|
||||
p.enqueueInterrupt("private", 10002, 0, 2, "小红", "b", false, false)
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
|
||||
if len(*got) != 2 {
|
||||
t.Fatalf("不同发送者不该合并,应有 2 次中断,实际 %d: %#v", len(*got), *got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatchWindowZeroFallsBackToPerMessage(t *testing.T) {
|
||||
p := newPermissionTestPlugin(t)
|
||||
got := collectInterrupts(p)
|
||||
p.batchWindow = 0
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
p.enqueueInterrupt("private", 10001, 0, int64(i), "小明", "原文", false, false)
|
||||
}
|
||||
if len(*got) != 3 {
|
||||
t.Fatalf("关闭合并时应逐条投递(3 次),实际 %d: %#v", len(*got), *got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingleMessageKeepsOriginalText(t *testing.T) {
|
||||
p := newPermissionTestPlugin(t)
|
||||
got := collectInterrupts(p)
|
||||
p.batchWindow = 20 * time.Millisecond
|
||||
p.batchMax = time.Second
|
||||
|
||||
p.enqueueInterrupt("group", 10001, 20002, 7, "小明", "单条原文", true, false)
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
|
||||
if len(*got) != 1 || (*got)[0] != "单条原文" {
|
||||
t.Fatalf("单条消息应沿用原文(含所有者前缀),实际 %#v", *got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user