diff --git a/internal/elf/elf_test.go b/internal/elf/elf_test.go index 7fd8024..78b0952 100644 --- a/internal/elf/elf_test.go +++ b/internal/elf/elf_test.go @@ -146,12 +146,12 @@ func TestManager_SpawnAndList(t *testing.T) { }) // Spawn two elfs - e1, err := mgr.Spawn(context.Background(), router.TaskGeneration, "task 1", "you are elf 1") + e1, err := mgr.Spawn(context.Background(), router.TaskGeneration, "task 1", "you are elf 1", 30) if err != nil { t.Fatalf("Spawn 1: %v", err) } - e2, err := mgr.Spawn(context.Background(), router.TaskReview, "task 2", "you are elf 2") + e2, err := mgr.Spawn(context.Background(), router.TaskReview, "task 2", "you are elf 2", 30) if err != nil { t.Fatalf("Spawn 2: %v", err) } @@ -202,9 +202,9 @@ func TestManager_WaitAll(t *testing.T) { mgr := NewManager(ManagerConfig{Router: rtr, Tools: tool.NewRegistry()}) - mgr.Spawn(context.Background(), router.TaskGeneration, "a", "") - mgr.Spawn(context.Background(), router.TaskGeneration, "b", "") - mgr.Spawn(context.Background(), router.TaskGeneration, "c", "") + mgr.Spawn(context.Background(), router.TaskGeneration, "a", "", 30) + mgr.Spawn(context.Background(), router.TaskGeneration, "b", "", 30) + mgr.Spawn(context.Background(), router.TaskGeneration, "c", "", 30) results := mgr.WaitAll() if len(results) != 3 { diff --git a/internal/elf/manager.go b/internal/elf/manager.go index 69b4c9a..b9db27e 100644 --- a/internal/elf/manager.go +++ b/internal/elf/manager.go @@ -42,7 +42,7 @@ func NewManager(cfg ManagerConfig) *Manager { // Spawn creates a new background elf with a router-selected provider. // The elf gets its own engine, history, and tools — no shared state. -func (m *Manager) Spawn(ctx context.Context, taskType router.TaskType, prompt, systemPrompt string) (Elf, error) { +func (m *Manager) Spawn(ctx context.Context, taskType router.TaskType, prompt, systemPrompt string, maxTurns int) (Elf, error) { // Ask router for the best arm for this task type task := router.Task{ Type: taskType, @@ -69,7 +69,7 @@ func (m *Manager) Spawn(ctx context.Context, taskType router.TaskType, prompt, s Tools: m.tools, System: systemPrompt, Model: arm.ModelName, - MaxTurns: 20, + MaxTurns: maxTurns, Logger: m.logger, }) if err != nil { @@ -87,13 +87,13 @@ func (m *Manager) Spawn(ctx context.Context, taskType router.TaskType, prompt, s } // SpawnWithProvider creates an elf using a specific provider (bypasses router). -func (m *Manager) SpawnWithProvider(prov provider.Provider, model, prompt, systemPrompt string) (Elf, error) { +func (m *Manager) SpawnWithProvider(prov provider.Provider, model, prompt, systemPrompt string, maxTurns int) (Elf, error) { eng, err := engine.New(engine.Config{ Provider: prov, Tools: m.tools, System: systemPrompt, Model: model, - MaxTurns: 20, + MaxTurns: maxTurns, Logger: m.logger, }) if err != nil { diff --git a/internal/tool/agent/agent.go b/internal/tool/agent/agent.go index e31f19c..303cec0 100644 --- a/internal/tool/agent/agent.go +++ b/internal/tool/agent/agent.go @@ -28,6 +28,10 @@ var paramSchema = json.RawMessage(`{ "wait": { "type": "boolean", "description": "Wait for the elf to complete (default true)" + }, + "max_turns": { + "type": "integer", + "description": "Maximum tool-calling rounds for the elf (default 30)" } }, "required": ["prompt"] @@ -58,6 +62,7 @@ type agentArgs struct { Prompt string `json:"prompt"` TaskType string `json:"task_type,omitempty"` Wait *bool `json:"wait,omitempty"` + MaxTurns int `json:"max_turns,omitempty"` } func (t *Tool) Execute(ctx context.Context, args json.RawMessage) (tool.Result, error) { @@ -74,10 +79,14 @@ func (t *Tool) Execute(ctx context.Context, args json.RawMessage) (tool.Result, if a.Wait != nil { wait = *a.Wait } + maxTurns := a.MaxTurns + if maxTurns <= 0 { + maxTurns = 30 // default + } systemPrompt := "You are an elf — a focused sub-agent of gnoma. Complete the given task thoroughly and concisely. Use tools as needed." - e, err := t.manager.Spawn(ctx, taskType, a.Prompt, systemPrompt) + e, err := t.manager.Spawn(ctx, taskType, a.Prompt, systemPrompt, maxTurns) if err != nil { return tool.Result{Output: fmt.Sprintf("Failed to spawn elf: %v", err)}, nil }