mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-11 03:13:24 +00:00
591 lines
19 KiB
Go
591 lines
19 KiB
Go
package channels
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/bus"
|
|
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
|
|
)
|
|
|
|
func TestHandleAgentEvent_QuickAckNonStreamingOnly(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeFixedTemplate,
|
|
MinDelayMs: 0,
|
|
Templates: []string{"On it."},
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", map[string]string{"local_key": "chat-1/topic"}, uuid.Nil, false, false, true, behavior)
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected quick acknowledgement outbound message")
|
|
}
|
|
if got.Content != "On it." || got.ChatID != "chat-1" || got.Metadata["local_key"] != "chat-1/topic" {
|
|
t.Fatalf("quick ack outbound = %+v, want content and routing metadata", got)
|
|
}
|
|
|
|
mb = bus.New()
|
|
mgr = NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-2", "test", "chat-1", "msg-1", nil, uuid.Nil, true, false, true, behavior)
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-2", nil)
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("streaming run emitted quick ack: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestUnregisterRun_CancelsPendingQuickAck(t *testing.T) {
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeFixedTemplate,
|
|
MinDelayMs: 500,
|
|
Templates: []string{"On it."},
|
|
},
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
mgr.UnregisterRun("run-1")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 75*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("unregistered run emitted quick ack: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestCancelQuickAck_BlocksInFlightSend(t *testing.T) {
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
rc := &RunContext{
|
|
ChannelName: "test",
|
|
ChatID: "chat-1",
|
|
ChatBehavior: ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeFixedTemplate,
|
|
Templates: []string{"On it."},
|
|
},
|
|
},
|
|
}
|
|
|
|
mgr.cancelQuickAck(rc)
|
|
mgr.sendQuickAck(rc)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("cancelled quick ack emitted message: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_GeneratedProgressCancelsFallback(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
IntermediateReplies: ResolvedIntermediateRepliesConfig{
|
|
Enabled: true,
|
|
Mode: IntermediateModeSidecar,
|
|
MaxTokens: 20,
|
|
MaxChars: 120,
|
|
},
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeLLMGenerated,
|
|
MinDelayMs: 500,
|
|
Templates: []string{"Fallback."},
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", map[string]string{"local_key": "chat-1/topic"}, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
ProgressGenerator: fakeDeliveryGenerator{content: "Mình đang kiểm tra tiếp."},
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
mgr.HandleAgentEvent(protocol.AgentEventToolCall, "run-1", map[string]string{"name": "skill_search"})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected generated progress outbound message")
|
|
}
|
|
if got.Content != "Mình đang kiểm tra tiếp." || got.Metadata["local_key"] != "chat-1/topic" {
|
|
t.Fatalf("generated progress outbound = %+v, want generated content and routing metadata", got)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), 75*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("fallback emitted after generated progress: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_ToolStatusMessagesRetired(t *testing.T) {
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, ResolvedChatBehavior{})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventToolCall, "run-1", map[string]string{"name": "skill_search"})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("tool status emitted outbound message: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_GeneratedQuickAckDoesNotUseTemplateWithoutGenerator(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeLLMGenerated,
|
|
MinDelayMs: 0,
|
|
Templates: []string{"Fallback."},
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, behavior)
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("generated quick ack used template without generator: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_GeneratedQuickAckUsesSidecarGenerator(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeLLMGenerated,
|
|
MinDelayMs: 0,
|
|
MaxTokens: 20,
|
|
MaxChars: 80,
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
QuickAckGenerator: fakeDeliveryGenerator{content: "Mình nhận rồi, để mình xử lý."},
|
|
Inbound: "kiểm tra giúp tôi",
|
|
Locale: "vi",
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected generated quick acknowledgement")
|
|
}
|
|
if got.Content != "Mình nhận rồi, để mình xử lý." {
|
|
t.Fatalf("quick ack content = %q, want generated content", got.Content)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_GeneratedQuickAckReceivesPersonaBrief(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeLLMGenerated,
|
|
MinDelayMs: 0,
|
|
MaxTokens: 20,
|
|
MaxChars: 80,
|
|
},
|
|
}
|
|
|
|
requests := make(chan DeliveryMessageRequest, 1)
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
QuickAckGenerator: captureDeliveryGenerator{content: "Mình nhận rồi.", requests: requests},
|
|
Inbound: "kiểm tra giúp tôi",
|
|
Locale: "vi",
|
|
PersonaBrief: "Style: concise, warm",
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
if _, ok := mb.SubscribeOutbound(ctx); !ok {
|
|
t.Fatal("expected generated quick acknowledgement")
|
|
}
|
|
|
|
select {
|
|
case got := <-requests:
|
|
if got.PersonaBrief != "Style: concise, warm" {
|
|
t.Fatalf("quick ack persona brief = %q, want runtime persona", got.PersonaBrief)
|
|
}
|
|
if got.Purpose != DeliveryPurposeQuickAck {
|
|
t.Fatalf("quick ack purpose = %q", got.Purpose)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("generator did not receive quick ack request")
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_GeneratedQuickAckDoesNotUseTemplateWhenGeneratorFails(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeSidecar,
|
|
MinDelayMs: 0,
|
|
MaxTokens: 20,
|
|
MaxChars: 120,
|
|
Templates: []string{defaultAckTemplate},
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
QuickAckGenerator: fakeDeliveryGenerator{err: errors.New("sidecar timeout")},
|
|
Inbound: "kiểm tra giúp tôi",
|
|
Locale: "vi",
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("generated quick ack used template after generator failure: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_IntermediateProgressDoesNotUseFallbackWhenGeneratorFails(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
IntermediateReplies: ResolvedIntermediateRepliesConfig{
|
|
Enabled: true,
|
|
Mode: IntermediateModeSidecar,
|
|
MaxTokens: 20,
|
|
MaxChars: 120,
|
|
},
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: false,
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", map[string]string{"local_key": "chat-1/topic"}, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
ProgressGenerator: fakeDeliveryGenerator{err: errors.New("sidecar timeout")},
|
|
Inbound: "kiểm tra giúp tôi",
|
|
Locale: "vi",
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
mgr.HandleAgentEvent(protocol.AgentEventToolCall, "run-1", map[string]string{"name": "skill_search"})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("intermediate progress used fallback after generator failure: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_IntermediateProgressReceivesPersonaBrief(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
IntermediateReplies: ResolvedIntermediateRepliesConfig{
|
|
Enabled: true,
|
|
Mode: IntermediateModeSidecar,
|
|
MaxTokens: 20,
|
|
MaxChars: 120,
|
|
},
|
|
QuickAck: ResolvedQuickAckConfig{Enabled: false},
|
|
}
|
|
|
|
requests := make(chan DeliveryMessageRequest, 1)
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
ProgressGenerator: captureDeliveryGenerator{content: "Đang soi tiếp.", requests: requests},
|
|
Inbound: "kiểm tra giúp tôi",
|
|
Locale: "vi",
|
|
PersonaBrief: "Style: concise, warm",
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventToolCall, "run-1", map[string]string{"name": "skill_search"})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
if _, ok := mb.SubscribeOutbound(ctx); !ok {
|
|
t.Fatal("expected generated progress outbound message")
|
|
}
|
|
|
|
select {
|
|
case got := <-requests:
|
|
if got.PersonaBrief != "Style: concise, warm" {
|
|
t.Fatalf("progress persona brief = %q, want runtime persona", got.PersonaBrief)
|
|
}
|
|
if got.Purpose != DeliveryPurposeProgress {
|
|
t.Fatalf("progress purpose = %q", got.Purpose)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("generator did not receive progress request")
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_FixedQuickAckPreservesInitialExplicitBlockReply(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeFixedTemplate,
|
|
MinDelayMs: 50,
|
|
Templates: []string{"Fallback."},
|
|
},
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, true, true, behavior)
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
mgr.HandleAgentEvent(protocol.AgentEventBlockReply, "run-1", map[string]string{"content": "Explicit block reply."})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected explicit block reply outbound message")
|
|
}
|
|
if got.Content != "Explicit block reply." {
|
|
t.Fatalf("explicit block reply content = %q", got.Content)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), 75*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("fallback emitted after explicit block reply: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_QuickAckDisabledSuppressesInitialExplicitBlockReply(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
quick ResolvedQuickAckConfig
|
|
}{
|
|
{
|
|
name: "enabled_false",
|
|
quick: ResolvedQuickAckConfig{Enabled: false, Templates: []string{"Fallback."}},
|
|
},
|
|
{
|
|
name: "mode_off",
|
|
quick: ResolvedQuickAckConfig{Enabled: true, Mode: QuickAckModeOff, Templates: []string{"Fallback."}},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: tc.quick,
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, true, true, behavior)
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
mgr.HandleAgentEvent(protocol.AgentEventBlockReply, "run-1", map[string]string{"content": "Initial quick acknowledgement."})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond)
|
|
defer cancel()
|
|
if got, ok := mb.SubscribeOutbound(ctx); ok {
|
|
t.Fatalf("initial explicit block reply emitted with quick ack disabled: %+v", got)
|
|
}
|
|
|
|
delivered, last := mgr.InterimDeliverySnapshot("run-1")
|
|
if delivered != 0 || last != "" {
|
|
t.Fatalf("interim delivery snapshot = (%d, %q), want none", delivered, last)
|
|
}
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventBlockReply, "run-1", map[string]string{"content": "Second iteration update."})
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected second explicit block reply outbound message")
|
|
}
|
|
if got.Content != "Second iteration update." {
|
|
t.Fatalf("explicit block reply content = %q", got.Content)
|
|
}
|
|
delivered, last = mgr.InterimDeliverySnapshot("run-1")
|
|
if delivered != 1 || last != "Second iteration update." {
|
|
t.Fatalf("interim delivery snapshot = (%d, %q), want delivered second reply", delivered, last)
|
|
}
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunCompleted, "run-1", nil)
|
|
delivered, last = mgr.InterimDeliverySnapshot("run-1")
|
|
if delivered != 1 || last != "Second iteration update." {
|
|
t.Fatalf("completed interim delivery snapshot = (%d, %q), want preserved second reply until unregister", delivered, last)
|
|
}
|
|
mgr.UnregisterRun("run-1")
|
|
delivered, last = mgr.InterimDeliverySnapshot("run-1")
|
|
if delivered != 0 || last != "" {
|
|
t.Fatalf("unregistered interim delivery snapshot = (%d, %q), want none", delivered, last)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_ToolAnnouncementBypassesInitialQuickAckSuppression(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
quick ResolvedQuickAckConfig
|
|
}{
|
|
{
|
|
name: "enabled_false",
|
|
quick: ResolvedQuickAckConfig{Enabled: false, Templates: []string{"Fallback."}},
|
|
},
|
|
{
|
|
name: "mode_off",
|
|
quick: ResolvedQuickAckConfig{Enabled: true, Mode: QuickAckModeOff, Templates: []string{"Fallback."}},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: tc.quick,
|
|
}
|
|
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithBehavior("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, true, true, behavior)
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
mgr.HandleAgentEvent(protocol.AgentEventBlockReply, "run-1", map[string]string{
|
|
"content": "Tôi sẽ dùng `skill_search` để xử lý bước tiếp theo.",
|
|
"source": protocol.BlockReplySourceToolAnnouncement,
|
|
})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected tool announcement outbound message")
|
|
}
|
|
if got.Content != "Tôi sẽ dùng `skill_search` để xử lý bước tiếp theo." {
|
|
t.Fatalf("tool announcement content = %q", got.Content)
|
|
}
|
|
|
|
delivered, last := mgr.InterimDeliverySnapshot("run-1")
|
|
if delivered != 1 || last != got.Content {
|
|
t.Fatalf("interim delivery snapshot = (%d, %q), want delivered announcement", delivered, last)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHandleAgentEvent_FixedQuickAckIgnoresPersonaBrief(t *testing.T) {
|
|
behavior := ResolvedChatBehavior{
|
|
Enabled: true,
|
|
QuickAck: ResolvedQuickAckConfig{
|
|
Enabled: true,
|
|
Mode: QuickAckModeFixedTemplate,
|
|
MinDelayMs: 0,
|
|
MaxChars: 120,
|
|
Templates: []string{"Checking the tool result now."},
|
|
},
|
|
}
|
|
|
|
requests := make(chan DeliveryMessageRequest, 1)
|
|
mb := bus.New()
|
|
mgr := NewManager(mb)
|
|
mgr.RegisterChannel("test", &chatBehaviorTestChannel{name: "test"})
|
|
mgr.RegisterRunWithDelivery("run-1", "test", "chat-1", "msg-1", nil, uuid.Nil, false, false, true, behavior, DeliveryRuntime{
|
|
QuickAckGenerator: captureDeliveryGenerator{content: "Generated.", requests: requests},
|
|
PersonaBrief: "Style: concise, warm",
|
|
})
|
|
|
|
mgr.HandleAgentEvent(protocol.AgentEventRunStarted, "run-1", nil)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
got, ok := mb.SubscribeOutbound(ctx)
|
|
if !ok {
|
|
t.Fatal("expected fixed quick acknowledgement")
|
|
}
|
|
if got.Content != "Checking the tool result now." {
|
|
t.Fatalf("fixed quick ack content = %q, want template", got.Content)
|
|
}
|
|
|
|
select {
|
|
case req := <-requests:
|
|
t.Fatalf("fixed quick ack unexpectedly called generator with request %+v", req)
|
|
default:
|
|
}
|
|
}
|
|
|
|
type fakeDeliveryGenerator struct {
|
|
content string
|
|
err error
|
|
}
|
|
|
|
func (g fakeDeliveryGenerator) GenerateDeliveryMessage(context.Context, DeliveryMessageRequest) (string, error) {
|
|
return g.content, g.err
|
|
}
|
|
|
|
type captureDeliveryGenerator struct {
|
|
content string
|
|
requests chan<- DeliveryMessageRequest
|
|
}
|
|
|
|
func (g captureDeliveryGenerator) GenerateDeliveryMessage(_ context.Context, req DeliveryMessageRequest) (string, error) {
|
|
if g.requests != nil {
|
|
g.requests <- req
|
|
}
|
|
return g.content, nil
|
|
}
|