2026-04-14 19:05:05 +08:00
|
|
|
package executor
|
|
|
|
|
|
|
|
|
|
import (
|
2026-05-10 00:02:23 +08:00
|
|
|
"encoding/json"
|
2026-04-14 19:05:05 +08:00
|
|
|
"testing"
|
|
|
|
|
|
2026-05-30 21:19:36 +08:00
|
|
|
"cs-ai-agent/internal/ai/runtime/tooling"
|
|
|
|
|
"cs-ai-agent/internal/pkg/toolx"
|
2026-04-14 19:05:05 +08:00
|
|
|
|
|
|
|
|
"github.com/cloudwego/eino/adk"
|
|
|
|
|
"github.com/cloudwego/eino/schema"
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-10 00:02:23 +08:00
|
|
|
func TestConsumeAgentEventsIgnoresPlainGraphToolText(t *testing.T) {
|
2026-04-14 19:05:05 +08:00
|
|
|
summary := &RunResult{
|
|
|
|
|
Status: "started",
|
|
|
|
|
InvokedToolCodes: make([]string, 0),
|
|
|
|
|
}
|
|
|
|
|
events, gen := adk.NewAsyncIteratorPair[*adk.AgentEvent]()
|
|
|
|
|
gen.Send(&adk.AgentEvent{
|
|
|
|
|
Output: &adk.AgentOutput{
|
|
|
|
|
MessageOutput: &adk.MessageVariant{
|
|
|
|
|
Role: schema.Tool,
|
|
|
|
|
ToolName: toolx.GraphHandoffConversation.Name,
|
|
|
|
|
Message: &schema.Message{
|
|
|
|
|
Content: "已为你转接人工客服,请稍候。,请稍候。",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
gen.Close()
|
|
|
|
|
|
|
|
|
|
consumeAgentEvents(events, summary, nil, map[string]string{
|
|
|
|
|
toolx.GraphHandoffConversation.Name: toolx.GraphHandoffConversation.Code,
|
|
|
|
|
})
|
|
|
|
|
|
2026-05-10 00:02:23 +08:00
|
|
|
if summary.ReplyText != "" {
|
2026-04-14 19:05:05 +08:00
|
|
|
t.Fatalf("unexpected reply text: %q", summary.ReplyText)
|
|
|
|
|
}
|
|
|
|
|
if summary.Status != "completed" {
|
|
|
|
|
t.Fatalf("unexpected summary status: %q", summary.Status)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-08 23:13:32 +08:00
|
|
|
|
2026-05-10 00:02:23 +08:00
|
|
|
func TestConsumeAgentEventsUsesGraphToolResultReplyText(t *testing.T) {
|
2026-05-08 23:13:32 +08:00
|
|
|
summary := &RunResult{
|
|
|
|
|
Status: "started",
|
|
|
|
|
InvokedToolCodes: make([]string, 0),
|
|
|
|
|
}
|
2026-05-10 00:02:23 +08:00
|
|
|
payload, err := json.Marshal(tooling.ToolResult{
|
|
|
|
|
Handled: true,
|
|
|
|
|
Terminal: true,
|
|
|
|
|
Action: "off_hours_handoff",
|
|
|
|
|
ReplyText: "当前暂不在人工客服服务时间内,你可以先继续描述问题。",
|
|
|
|
|
ShouldRetry: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("marshal graph tool result: %v", err)
|
|
|
|
|
}
|
2026-05-08 23:13:32 +08:00
|
|
|
events, gen := adk.NewAsyncIteratorPair[*adk.AgentEvent]()
|
|
|
|
|
gen.Send(&adk.AgentEvent{
|
|
|
|
|
Output: &adk.AgentOutput{
|
|
|
|
|
MessageOutput: &adk.MessageVariant{
|
|
|
|
|
Role: schema.Tool,
|
|
|
|
|
ToolName: toolx.GraphHandoffConversation.Name,
|
|
|
|
|
Message: &schema.Message{
|
2026-05-10 00:02:23 +08:00
|
|
|
Content: string(payload),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
gen.Send(&adk.AgentEvent{
|
|
|
|
|
Output: &adk.AgentOutput{
|
|
|
|
|
MessageOutput: &adk.MessageVariant{
|
|
|
|
|
Role: schema.Assistant,
|
|
|
|
|
Message: &schema.Message{
|
|
|
|
|
Content: "我再试一次转人工。",
|
2026-05-08 23:13:32 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
gen.Close()
|
|
|
|
|
|
|
|
|
|
consumeAgentEvents(events, summary, nil, map[string]string{
|
|
|
|
|
toolx.GraphHandoffConversation.Name: toolx.GraphHandoffConversation.Code,
|
|
|
|
|
})
|
|
|
|
|
|
2026-05-10 00:02:23 +08:00
|
|
|
if summary.ReplyText != "当前暂不在人工客服服务时间内,你可以先继续描述问题。" {
|
|
|
|
|
t.Fatalf("unexpected reply text: %q", summary.ReplyText)
|
2026-05-08 23:13:32 +08:00
|
|
|
}
|
|
|
|
|
if summary.Status != "completed" {
|
|
|
|
|
t.Fatalf("unexpected summary status: %q", summary.Status)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-08 23:26:25 +08:00
|
|
|
|
2026-05-10 00:02:23 +08:00
|
|
|
func TestConsumeAgentEventsSuppressesGraphToolResultWhenReplyAlreadySent(t *testing.T) {
|
2026-05-08 23:26:25 +08:00
|
|
|
summary := &RunResult{
|
|
|
|
|
Status: "started",
|
|
|
|
|
InvokedToolCodes: make([]string, 0),
|
|
|
|
|
}
|
2026-05-10 00:02:23 +08:00
|
|
|
payload, err := json.Marshal(tooling.ToolResult{
|
|
|
|
|
Handled: true,
|
|
|
|
|
Terminal: true,
|
|
|
|
|
Action: "off_hours_handoff",
|
|
|
|
|
ReplyText: "当前暂不在人工客服服务时间内,你可以先继续描述问题。",
|
|
|
|
|
ReplySent: true,
|
|
|
|
|
ShouldRetry: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("marshal graph tool result: %v", err)
|
|
|
|
|
}
|
2026-05-08 23:26:25 +08:00
|
|
|
events, gen := adk.NewAsyncIteratorPair[*adk.AgentEvent]()
|
|
|
|
|
gen.Send(&adk.AgentEvent{
|
|
|
|
|
Output: &adk.AgentOutput{
|
|
|
|
|
MessageOutput: &adk.MessageVariant{
|
|
|
|
|
Role: schema.Tool,
|
|
|
|
|
ToolName: toolx.GraphHandoffConversation.Name,
|
|
|
|
|
Message: &schema.Message{
|
2026-05-10 00:02:23 +08:00
|
|
|
Content: string(payload),
|
2026-05-08 23:26:25 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
gen.Send(&adk.AgentEvent{
|
|
|
|
|
Output: &adk.AgentOutput{
|
|
|
|
|
MessageOutput: &adk.MessageVariant{
|
|
|
|
|
Role: schema.Assistant,
|
|
|
|
|
Message: &schema.Message{
|
2026-05-10 00:02:23 +08:00
|
|
|
Content: "我再试一次转人工。",
|
2026-05-08 23:26:25 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
gen.Close()
|
|
|
|
|
|
|
|
|
|
consumeAgentEvents(events, summary, nil, map[string]string{
|
|
|
|
|
toolx.GraphHandoffConversation.Name: toolx.GraphHandoffConversation.Code,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if summary.ReplyText != "" {
|
2026-05-10 00:02:23 +08:00
|
|
|
t.Fatalf("expected no committed reply because graph already sent it, got %q", summary.ReplyText)
|
|
|
|
|
}
|
|
|
|
|
if summary.Status != "completed" {
|
|
|
|
|
t.Fatalf("unexpected summary status: %q", summary.Status)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConsumeAgentEventsCompletesGraphToolWithNoVisibleReply(t *testing.T) {
|
|
|
|
|
summary := &RunResult{
|
|
|
|
|
Status: "started",
|
|
|
|
|
InvokedToolCodes: make([]string, 0),
|
|
|
|
|
}
|
|
|
|
|
events, gen := adk.NewAsyncIteratorPair[*adk.AgentEvent]()
|
|
|
|
|
gen.Send(&adk.AgentEvent{
|
|
|
|
|
Output: &adk.AgentOutput{
|
|
|
|
|
MessageOutput: &adk.MessageVariant{
|
|
|
|
|
Role: schema.Tool,
|
|
|
|
|
ToolName: toolx.GraphHandoffConversation.Name,
|
|
|
|
|
Message: &schema.Message{
|
|
|
|
|
Content: "",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
gen.Close()
|
|
|
|
|
|
|
|
|
|
consumeAgentEvents(events, summary, nil, map[string]string{
|
|
|
|
|
toolx.GraphHandoffConversation.Name: toolx.GraphHandoffConversation.Code,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if summary.ReplyText != "" {
|
|
|
|
|
t.Fatalf("expected no reply text, got %q", summary.ReplyText)
|
2026-05-08 23:26:25 +08:00
|
|
|
}
|
|
|
|
|
if summary.Status != "completed" {
|
|
|
|
|
t.Fatalf("unexpected summary status: %q", summary.Status)
|
|
|
|
|
}
|
|
|
|
|
}
|