mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 15:40:38 +02:00
153 lines
4.5 KiB
Go
153 lines
4.5 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/cloudwego/eino/schema"
|
|
)
|
|
|
|
func TestEinoStreamToolCallCompletionHandlerMergesEmitsAndPersistsToolCalls(t *testing.T) {
|
|
idx := 0
|
|
var eventTypes []string
|
|
var marked []toolCallPendingInfo
|
|
progress := func(eventType, _ string, _ interface{}) {
|
|
eventTypes = append(eventTypes, eventType)
|
|
}
|
|
runMessages := newEinoRunMessageAccumulator(nil)
|
|
runProgress := newEinoRunProgressTracker(
|
|
"deep", "lead", "conv-1", progress,
|
|
func(agent string) bool { return agent == "lead" },
|
|
nil,
|
|
)
|
|
handler := newEinoStreamToolCallCompletionHandler(einoStreamToolCallCompletionHandlerConfig{
|
|
ConversationID: "conv-1",
|
|
OrchMode: "deep",
|
|
Progress: progress,
|
|
RunProgress: runProgress,
|
|
RunMessages: runMessages,
|
|
MarkPending: func(info toolCallPendingInfo) {
|
|
marked = append(marked, info)
|
|
},
|
|
})
|
|
|
|
chunk := handler.Complete([]schema.ToolCall{
|
|
{
|
|
ID: "call-1",
|
|
Type: "function",
|
|
Index: &idx,
|
|
Function: schema.FunctionCall{
|
|
Name: "execute",
|
|
Arguments: `{"command":`,
|
|
},
|
|
},
|
|
{
|
|
Index: &idx,
|
|
Function: schema.FunctionCall{
|
|
Arguments: `"pwd"}`,
|
|
},
|
|
},
|
|
}, "lead")
|
|
|
|
if chunk == nil || len(chunk.ToolCalls) != 1 {
|
|
t.Fatalf("merged chunk = %#v, want one tool call", chunk)
|
|
}
|
|
if got := chunk.ToolCalls[0].Function.Arguments; got != `{"command":"pwd"}` {
|
|
t.Fatalf("arguments = %q", got)
|
|
}
|
|
msgs := runMessages.Messages()
|
|
if len(msgs) != 1 || len(msgs[0].ToolCalls) != 1 {
|
|
t.Fatalf("run messages = %#v, want persisted assistant tool call", msgs)
|
|
}
|
|
if len(marked) != 1 || marked[0].ToolCallID != "call-1" || marked[0].ToolName != "execute" {
|
|
t.Fatalf("marked pending = %#v", marked)
|
|
}
|
|
if !containsString(eventTypes, "tool_call") {
|
|
t.Fatalf("event types = %#v, want tool_call", eventTypes)
|
|
}
|
|
}
|
|
|
|
func TestEinoStreamToolCallCompletionHandlerPreservesStreamingToolArgumentsForToolLayerRecovery(t *testing.T) {
|
|
idx := 0
|
|
var eventTypes []string
|
|
var marked []toolCallPendingInfo
|
|
progress := func(eventType, _ string, _ interface{}) {
|
|
eventTypes = append(eventTypes, eventType)
|
|
}
|
|
runMessages := newEinoRunMessageAccumulator(nil)
|
|
runProgress := newEinoRunProgressTracker(
|
|
"deep", "lead", "conv-1", progress,
|
|
func(agent string) bool { return agent == "lead" },
|
|
nil,
|
|
)
|
|
handler := newEinoStreamToolCallCompletionHandler(einoStreamToolCallCompletionHandlerConfig{
|
|
ConversationID: "conv-1",
|
|
OrchMode: "deep",
|
|
Progress: progress,
|
|
RunProgress: runProgress,
|
|
RunMessages: runMessages,
|
|
MarkPending: func(info toolCallPendingInfo) {
|
|
marked = append(marked, info)
|
|
},
|
|
})
|
|
|
|
chunk := handler.Complete([]schema.ToolCall{
|
|
{
|
|
ID: "call-stream-unsafe",
|
|
Type: "function",
|
|
Index: &idx,
|
|
Function: schema.FunctionCall{
|
|
Name: "execute",
|
|
Arguments: `{"command":"`,
|
|
},
|
|
},
|
|
{
|
|
Index: &idx,
|
|
Function: schema.FunctionCall{
|
|
Arguments: strings.Repeat("x", 256) + `"}`,
|
|
},
|
|
},
|
|
}, "lead")
|
|
|
|
if chunk == nil || len(chunk.ToolCalls) != 1 {
|
|
t.Fatalf("chunk = %#v, want one tool call", chunk)
|
|
}
|
|
args := chunk.ToolCalls[0].Function.Arguments
|
|
if !strings.Contains(args, strings.Repeat("x", 32)) {
|
|
t.Fatalf("streaming arguments were unexpectedly rewritten: %q", args)
|
|
}
|
|
msgs := runMessages.Messages()
|
|
if len(msgs) != 1 || len(msgs[0].ToolCalls) != 1 {
|
|
t.Fatalf("run messages = %#v, want assistant tool call", msgs)
|
|
}
|
|
if got := msgs[0].ToolCalls[0].Function.Arguments; got != args {
|
|
t.Fatalf("persisted tool call arguments = %q, want %q", got, args)
|
|
}
|
|
if len(marked) != 1 || marked[0].ToolCallID != "call-stream-unsafe" || marked[0].ToolName != "execute" {
|
|
t.Fatalf("marked pending = %#v", marked)
|
|
}
|
|
if containsString(eventTypes, "model_output_rejected") || !containsString(eventTypes, "tool_call") {
|
|
t.Fatalf("event types = %#v, want real tool_call without model-output recovery", eventTypes)
|
|
}
|
|
}
|
|
|
|
func TestEinoStreamToolCallCompletionHandlerIgnoresEmptyFragments(t *testing.T) {
|
|
runMessages := newEinoRunMessageAccumulator(nil)
|
|
called := false
|
|
handler := newEinoStreamToolCallCompletionHandler(einoStreamToolCallCompletionHandlerConfig{
|
|
RunMessages: runMessages,
|
|
Progress: func(string, string, interface{}) {
|
|
called = true
|
|
},
|
|
})
|
|
if chunk := handler.Complete(nil, "lead"); chunk != nil {
|
|
t.Fatalf("chunk = %#v, want nil", chunk)
|
|
}
|
|
if len(runMessages.Messages()) != 0 {
|
|
t.Fatalf("run messages = %#v, want empty", runMessages.Messages())
|
|
}
|
|
if called {
|
|
t.Fatal("progress should not be called for empty fragments")
|
|
}
|
|
}
|