mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 23:50:32 +02:00
164 lines
5.1 KiB
Go
164 lines
5.1 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/cloudwego/eino/adk"
|
|
"github.com/cloudwego/eino/components/model"
|
|
"github.com/cloudwego/eino/schema"
|
|
)
|
|
|
|
type capturingAgenticChatModel struct {
|
|
mu sync.Mutex
|
|
inputs [][]*schema.AgenticMessage
|
|
output *schema.AgenticMessage
|
|
}
|
|
|
|
func (m *capturingAgenticChatModel) Generate(_ context.Context, input []*schema.AgenticMessage, _ ...model.Option) (*schema.AgenticMessage, error) {
|
|
m.mu.Lock()
|
|
m.inputs = append(m.inputs, input)
|
|
m.mu.Unlock()
|
|
if m.output != nil {
|
|
return m.output, nil
|
|
}
|
|
return &schema.AgenticMessage{
|
|
Role: schema.AgenticRoleTypeAssistant,
|
|
ContentBlocks: []*schema.ContentBlock{schema.NewContentBlock(&schema.AssistantGenText{Text: "agentic answer"})},
|
|
}, nil
|
|
}
|
|
|
|
func (m *capturingAgenticChatModel) Stream(_ context.Context, input []*schema.AgenticMessage, _ ...model.Option) (*schema.StreamReader[*schema.AgenticMessage], error) {
|
|
msg, err := m.Generate(context.Background(), input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return schema.StreamReaderFromArray([]*schema.AgenticMessage{msg}), nil
|
|
}
|
|
|
|
func (m *capturingAgenticChatModel) snapshotInputs() [][]*schema.AgenticMessage {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
out := make([][]*schema.AgenticMessage, len(m.inputs))
|
|
copy(out, m.inputs)
|
|
return out
|
|
}
|
|
|
|
func TestNewEinoAgenticChatModelAgentAdapterRunsThroughClassicAgentBoundary(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
trace := newModelFacingTraceHolder()
|
|
fakeModel := &capturingAgenticChatModel{}
|
|
agent, err := newEinoAgenticChatModelAgentAdapter(ctx, einoAgenticChatModelAgentConfig{
|
|
Name: "agentic",
|
|
Description: "agentic adapter test",
|
|
Instruction: "system instruction",
|
|
Model: fakeModel,
|
|
Handlers: appendEinoAgenticChatModelTailMiddlewares(nil, einoChatModelTailConfig{
|
|
phase: "agentic",
|
|
trace: trace,
|
|
}),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("newEinoAgenticChatModelAgentAdapter: %v", err)
|
|
}
|
|
|
|
iter := agent.Run(ctx, &adk.AgentInput{
|
|
Messages: []*schema.Message{schema.UserMessage("classic input")},
|
|
})
|
|
var last *adk.AgentEvent
|
|
for {
|
|
ev, ok := iter.Next()
|
|
if !ok {
|
|
break
|
|
}
|
|
if ev.Err != nil {
|
|
t.Fatalf("agent event error: %v", ev.Err)
|
|
}
|
|
last = ev
|
|
}
|
|
if last == nil || last.Output == nil || last.Output.MessageOutput == nil {
|
|
t.Fatalf("last event = %#v, want message output", last)
|
|
}
|
|
if got := last.Output.MessageOutput.Message.Content; got != "agentic answer" {
|
|
t.Fatalf("classic output content = %q, want agentic answer", got)
|
|
}
|
|
|
|
inputs := fakeModel.snapshotInputs()
|
|
if len(inputs) != 1 {
|
|
t.Fatalf("model calls = %d, want 1", len(inputs))
|
|
}
|
|
if len(inputs[0]) != 2 {
|
|
t.Fatalf("model input messages = %d, want instruction + user", len(inputs[0]))
|
|
}
|
|
if inputs[0][0].Role != schema.AgenticRoleTypeSystem || agenticMessageText(inputs[0][0]) != "system instruction" {
|
|
t.Fatalf("first agentic input = %#v", inputs[0][0])
|
|
}
|
|
if inputs[0][1].Role != schema.AgenticRoleTypeUser || agenticMessageText(inputs[0][1]) != "classic input" {
|
|
t.Fatalf("second agentic input = %#v", inputs[0][1])
|
|
}
|
|
|
|
snapshot := trace.Snapshot()
|
|
if len(snapshot) != 2 || snapshot[0].Role != schema.System || snapshot[1].Role != schema.User {
|
|
t.Fatalf("trace snapshot = %#v, want classic system + user trace", snapshot)
|
|
}
|
|
}
|
|
|
|
func TestNewEinoAgenticChatModelAgentAdapterPreservesTypedToolCallsForToolLayerRecovery(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
fakeModel := &capturingAgenticChatModel{
|
|
output: &schema.AgenticMessage{
|
|
Role: schema.AgenticRoleTypeAssistant,
|
|
ContentBlocks: []*schema.ContentBlock{schema.NewContentBlock(&schema.FunctionToolCall{
|
|
CallID: "call-1",
|
|
Name: "exec",
|
|
Arguments: `{"command":"` + strings.Repeat("x", 20000) + `"}`,
|
|
})},
|
|
},
|
|
}
|
|
agent, err := newEinoAgenticChatModelAgentAdapter(ctx, einoAgenticChatModelAgentConfig{
|
|
Name: "agentic",
|
|
Description: "agentic adapter test",
|
|
Model: fakeModel,
|
|
Handlers: appendEinoAgenticChatModelTailMiddlewares(nil, einoChatModelTailConfig{
|
|
phase: "agentic",
|
|
}),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("newEinoAgenticChatModelAgentAdapter: %v", err)
|
|
}
|
|
iter := agent.Run(ctx, &adk.AgentInput{Messages: []*schema.Message{schema.UserMessage("run")}})
|
|
var last *adk.AgentEvent
|
|
for {
|
|
ev, ok := iter.Next()
|
|
if !ok {
|
|
break
|
|
}
|
|
if ev.Err != nil {
|
|
t.Fatalf("agent event error: %v", ev.Err)
|
|
}
|
|
last = ev
|
|
}
|
|
if last == nil || last.Output == nil || last.Output.MessageOutput == nil {
|
|
t.Fatalf("last event = %#v, want message output", last)
|
|
}
|
|
msg := last.Output.MessageOutput.Message
|
|
if len(msg.ToolCalls) != 1 {
|
|
t.Fatalf("tool calls = %#v, want one tool call", msg.ToolCalls)
|
|
}
|
|
args := msg.ToolCalls[0].Function.Arguments
|
|
if !strings.Contains(args, strings.Repeat("x", 32)) || strings.Contains(args, modelOutputRecoveryKey) {
|
|
t.Fatalf("agentic tool args were unexpectedly rewritten: %q", args)
|
|
}
|
|
}
|
|
|
|
func TestNewEinoAgenticChatModelAgentAdapterRequiresModel(t *testing.T) {
|
|
t.Parallel()
|
|
if _, err := newEinoAgenticChatModelAgentAdapter(context.Background(), einoAgenticChatModelAgentConfig{}); err == nil {
|
|
t.Fatal("expected missing model error")
|
|
}
|
|
}
|