返回 DeepSeek-Reasonix
goal_boundaries_test.go
根目录 / internal / agent / goal_boundaries_test.go
1 package agent
2
3 import (
4 "context"
5 "fmt"
6 "testing"
7
8 "reasonix/internal/agent/testutil"
9 "reasonix/internal/event"
10 "reasonix/internal/provider"
11 "reasonix/internal/tool"
12 )
13
14 func TestGoalRunHasNoDefaultModelRoundCeiling(t *testing.T) {
15 reg := tool.NewRegistry()
16 reg.Add(fakeTool{name: "read_file", readOnly: true})
17 turns := make([]testutil.Turn, 0, 102)
18 for i := range 101 {
19 turns = append(turns, testutil.Turn{ToolCalls: []provider.ToolCall{{
20 ID: fmt.Sprintf("r%d", i), Name: "read_file", Arguments: fmt.Sprintf(`{"path":"file-%d"}`, i),
21 }}})
22 }
23 turns = append(turns, testutil.Turn{Text: "Done."})
24 prov := testutil.NewMock("m", turns...)
25 a := New(prov, reg, NewSession(""), Options{}, event.Discard)
26 ctx := WithDeliveryExecutionScope(context.Background(), DeliveryExecutionScope{ID: "goal-1", TaskText: "work"})
27 if err := a.Run(ctx, "work"); err != nil {
28 t.Fatalf("Goal run stopped at a default round boundary: %v", err)
29 }
30 if prov.CallCount() != 102 {
31 t.Fatalf("provider calls = %d, want 101 tool rounds plus final", prov.CallCount())
32 }
33 }
34
35 func TestExplicitMaxStepsStillAppliesToGoal(t *testing.T) {
36 reg := tool.NewRegistry()
37 reg.Add(fakeTool{name: "read_file", readOnly: true})
38 prov := testutil.NewMock("m",
39 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "r1", Name: "read_file", Arguments: `{"path":"a"}`}}},
40 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "r2", Name: "read_file", Arguments: `{"path":"b"}`}}},
41 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "r3", Name: "read_file", Arguments: `{"path":"c"}`}}},
42 testutil.Turn{Text: "Done."},
43 )
44 a := New(prov, reg, NewSession(""), Options{MaxSteps: 3}, event.Discard)
45 ctx := WithDeliveryExecutionScope(context.Background(), DeliveryExecutionScope{ID: "goal-1", TaskText: "work"})
46 err := a.Run(ctx, "work")
47 info, ok := InspectRunPause(err)
48 if !ok || info.Kind != "max_steps" || info.HostOwned || info.Limit != 3 {
49 t.Fatalf("explicit MaxSteps pause = %+v ok=%v err=%v", info, ok, err)
50 }
51 if prov.CallCount() != 4 {
52 t.Fatalf("provider calls = %d, want explicit three rounds plus summary", prov.CallCount())
53 }
54 }
55
56 func TestUnboundedGoalParentLeavesChildUnbounded(t *testing.T) {
57 task := &TaskTool{}
58 if got := task.childMaxStepsForContext(context.Background(), 0); got != 0 {
59 t.Fatalf("child steps = %d, want unlimited", got)
60 }
61 if got := task.childMaxStepsForContext(context.Background(), 3); got != 3 {
62 t.Fatalf("explicit child steps = %d, want 3", got)
63 }
64 task.maxSteps = 16
65 if got := task.childMaxStepsForContext(context.Background(), 0); got != 8 {
66 t.Fatalf("explicit parent child steps = %d, want 8", got)
67 }
68 }
69
69 lines GO