feat: configurable max_turns for elfs — LLM sets via agent tool param

This commit is contained in:
2026-04-03 19:37:17 +02:00
parent 2ccc261c39
commit 97d5093526
3 changed files with 19 additions and 10 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
}