fix(message): suppress duplicate sends within same round
This commit is contained in:
parent
c4080436db
commit
a49803ec90
2 changed files with 50 additions and 0 deletions
|
|
@ -12,6 +12,9 @@ type MessageTool struct {
|
||||||
defaultChannel string
|
defaultChannel string
|
||||||
defaultChatID string
|
defaultChatID string
|
||||||
sentInRound bool // Tracks whether a message was sent in the current processing round
|
sentInRound bool // Tracks whether a message was sent in the current processing round
|
||||||
|
lastChannel string
|
||||||
|
lastChatID string
|
||||||
|
lastContent string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageTool() *MessageTool {
|
func NewMessageTool() *MessageTool {
|
||||||
|
|
@ -51,6 +54,9 @@ func (t *MessageTool) SetContext(channel, chatID string) {
|
||||||
t.defaultChannel = channel
|
t.defaultChannel = channel
|
||||||
t.defaultChatID = chatID
|
t.defaultChatID = chatID
|
||||||
t.sentInRound = false // Reset send tracking for new processing round
|
t.sentInRound = false // Reset send tracking for new processing round
|
||||||
|
t.lastChannel = ""
|
||||||
|
t.lastChatID = ""
|
||||||
|
t.lastContent = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSentInRound returns true if the message tool sent a message during the current round.
|
// HasSentInRound returns true if the message tool sent a message during the current round.
|
||||||
|
|
@ -86,6 +92,15 @@ func (t *MessageTool) Execute(ctx context.Context, args map[string]any) *ToolRes
|
||||||
return &ToolResult{ForLLM: "Message sending not configured", IsError: true}
|
return &ToolResult{ForLLM: "Message sending not configured", IsError: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guard against accidental repeated tool-calls in one LLM round.
|
||||||
|
// If the same target and content were already sent, suppress duplicate delivery.
|
||||||
|
if t.sentInRound && t.lastChannel == channel && t.lastChatID == chatID && t.lastContent == content {
|
||||||
|
return &ToolResult{
|
||||||
|
ForLLM: "Duplicate message suppressed in current round",
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := t.sendCallback(channel, chatID, content); err != nil {
|
if err := t.sendCallback(channel, chatID, content); err != nil {
|
||||||
return &ToolResult{
|
return &ToolResult{
|
||||||
ForLLM: fmt.Sprintf("sending message: %v", err),
|
ForLLM: fmt.Sprintf("sending message: %v", err),
|
||||||
|
|
@ -95,6 +110,9 @@ func (t *MessageTool) Execute(ctx context.Context, args map[string]any) *ToolRes
|
||||||
}
|
}
|
||||||
|
|
||||||
t.sentInRound = true
|
t.sentInRound = true
|
||||||
|
t.lastChannel = channel
|
||||||
|
t.lastChatID = chatID
|
||||||
|
t.lastContent = content
|
||||||
// Silent: user already received the message directly
|
// Silent: user already received the message directly
|
||||||
return &ToolResult{
|
return &ToolResult{
|
||||||
ForLLM: fmt.Sprintf("Message sent to %s:%s", channel, chatID),
|
ForLLM: fmt.Sprintf("Message sent to %s:%s", channel, chatID),
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,38 @@ func TestMessageTool_Execute_NotConfigured(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMessageTool_Execute_SuppressDuplicateInSameRound(t *testing.T) {
|
||||||
|
tool := NewMessageTool()
|
||||||
|
tool.SetContext("test-channel", "test-chat-id")
|
||||||
|
|
||||||
|
callCount := 0
|
||||||
|
tool.SetSendCallback(func(channel, chatID, content string) error {
|
||||||
|
callCount++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
args := map[string]any{
|
||||||
|
"content": "same message",
|
||||||
|
}
|
||||||
|
|
||||||
|
first := tool.Execute(ctx, args)
|
||||||
|
second := tool.Execute(ctx, args)
|
||||||
|
|
||||||
|
if callCount != 1 {
|
||||||
|
t.Fatalf("send callback call count = %d, want 1", callCount)
|
||||||
|
}
|
||||||
|
if !first.Silent || first.IsError {
|
||||||
|
t.Fatalf("first result unexpected: %+v", first)
|
||||||
|
}
|
||||||
|
if !second.Silent || second.IsError {
|
||||||
|
t.Fatalf("second result unexpected: %+v", second)
|
||||||
|
}
|
||||||
|
if second.ForLLM != "Duplicate message suppressed in current round" {
|
||||||
|
t.Fatalf("second ForLLM = %q", second.ForLLM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMessageTool_Name(t *testing.T) {
|
func TestMessageTool_Name(t *testing.T) {
|
||||||
tool := NewMessageTool()
|
tool := NewMessageTool()
|
||||||
if tool.Name() != "message" {
|
if tool.Name() != "message" {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue