e071084ebe
govulncheck / govulncheck (push) Waiting to run
Harness (E2E) / Harnesses (mock LLM) (push) Waiting to run
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Run Tests / Unit Tests (push) Waiting to run
Run Tests / Etcd Integration Tests (push) Waiting to run
72 lines
2.3 KiB
Go
72 lines
2.3 KiB
Go
package agent
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Repeating the same tool call with the same arguments is refused once it
|
|
// exceeds LoopLimit, and the model is told to change approach.
|
|
func TestLoopDetectionStopsRepeats(t *testing.T) {
|
|
a := newTestAgent(Name("looper"), LoopLimit(3))
|
|
h := a.toolHandler()
|
|
|
|
// First 3 identical calls are allowed (they fall through to RPC,
|
|
// which fails harmlessly — we only care they weren't refused as loops).
|
|
for i := 1; i <= 3; i++ {
|
|
content := toolContent(h, "demo_Svc_Do", map[string]any{"q": "x"})
|
|
if strings.Contains(content, "loop detected") {
|
|
t.Fatalf("call %d wrongly flagged as a loop", i)
|
|
}
|
|
}
|
|
|
|
// The 4th identical call is refused as a loop.
|
|
content := toolContent(h, "demo_Svc_Do", map[string]any{"q": "x"})
|
|
if !strings.Contains(content, "loop detected") {
|
|
t.Errorf("4th identical call should be refused as a loop; got %q", content)
|
|
}
|
|
}
|
|
|
|
// Different arguments are not a loop, even past the limit.
|
|
func TestLoopDetectionAllowsDistinctCalls(t *testing.T) {
|
|
a := newTestAgent(Name("distinct"), LoopLimit(2))
|
|
h := a.toolHandler()
|
|
|
|
for i := 0; i < 5; i++ {
|
|
content := toolContent(h, "demo_Svc_Do", map[string]any{"q": i}) // distinct args each time
|
|
if strings.Contains(content, "loop detected") {
|
|
t.Fatalf("distinct call %d wrongly flagged as a loop", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// LoopLimit(0) disables detection.
|
|
func TestLoopDetectionDisabled(t *testing.T) {
|
|
a := newTestAgent(Name("noloop"), LoopLimit(0))
|
|
h := a.toolHandler()
|
|
for i := 0; i < 6; i++ {
|
|
content := toolContent(h, "demo_Svc_Do", map[string]any{"q": "same"})
|
|
if strings.Contains(content, "loop detected") {
|
|
t.Fatalf("loop detection should be disabled with LoopLimit(0)")
|
|
}
|
|
}
|
|
}
|
|
|
|
// It defaults on (lenient) so repeated identical calls are caught without
|
|
// any configuration.
|
|
func TestLoopDetectionDefaultOn(t *testing.T) {
|
|
a := New(Name("d"), Provider("fake")).(*agentImpl)
|
|
a.setup()
|
|
if a.opts.LoopLimit <= 0 {
|
|
t.Fatalf("LoopLimit should default on, got %d", a.opts.LoopLimit)
|
|
}
|
|
h := a.toolHandler()
|
|
var lastContent string
|
|
for i := 0; i < a.opts.LoopLimit+1; i++ {
|
|
lastContent = toolContent(h, "demo_Svc_Do", map[string]any{})
|
|
}
|
|
if !strings.Contains(lastContent, "loop detected") {
|
|
t.Errorf("default loop detection should catch repeated calls; got %q", lastContent)
|
|
}
|
|
}
|