diff --git a/agent/loop.go b/agent/loop.go index 02790d7..c73f594 100644 --- a/agent/loop.go +++ b/agent/loop.go @@ -29,28 +29,20 @@ type Config struct { SystemPrompt string } -type Loop struct { - cfg Config -} - -func New(cfg Config) *Loop { - return &Loop{cfg: cfg} -} - -func (l *Loop) Run(ctx context.Context, state State) error { - maxSteps := l.cfg.MaxSteps +func Run(ctx context.Context, cfg Config, state State) error { + maxSteps := cfg.MaxSteps if maxSteps <= 0 { maxSteps = 1 } for step := range maxSteps { history := state.History() - if l.cfg.SystemPrompt != "" { - history = append([]backend.Message{{Role: "system", Content: l.cfg.SystemPrompt}}, history...) + if cfg.SystemPrompt != "" { + history = append([]backend.Message{{Role: "system", Content: cfg.SystemPrompt}}, history...) } // Stream the assistant's response. - ch, err := l.cfg.Backend.ChatStream(ctx, l.cfg.Model, history, l.cfg.Tools) + ch, err := cfg.Backend.ChatStream(ctx, cfg.Model, history, cfg.Tools) if err != nil { return fmt.Errorf("step %d: %w", step, err) } @@ -63,7 +55,7 @@ func (l *Loop) Run(ctx context.Context, state State) error { for ev := range ch { if ev.Content != "" { content.WriteString(ev.Content) - l.emit(OutputMsg{Role: "assistant", Content: ev.Content}) + emit(cfg, OutputMsg{Role: "assistant", Content: ev.Content}) } toolCalls = append(toolCalls, ev.ToolCalls...) if ev.Done { @@ -82,12 +74,12 @@ func (l *Loop) Run(ctx context.Context, state State) error { var results []ToolResult for _, tc := range toolCalls { - l.emit(OutputMsg{Role: "call", Name: tc.Name, Content: string(tc.Arguments)}) + emit(cfg, OutputMsg{Role: "call", Name: tc.Name, Content: string(tc.Arguments)}) var result string var isErr bool - if l.cfg.Exec != nil { - out, err := l.cfg.Exec(tc.Name, tc.Arguments) + if cfg.Exec != nil { + out, err := cfg.Exec(tc.Name, tc.Arguments) if err != nil { result = fmt.Sprintf("error: %v", err) isErr = true @@ -105,34 +97,34 @@ func (l *Loop) Run(ctx context.Context, state State) error { Content: result, IsError: isErr, }) - l.emit(OutputMsg{Role: "tool", Name: tc.Name, Content: result}) + emit(cfg, OutputMsg{Role: "tool", Name: tc.Name, Content: result}) } // Emit usage when we have real token counts. if usage.InputTokens > 0 || usage.OutputTokens > 0 { - l.emit(OutputMsg{Role: "usage", Usage: usage}) + emit(cfg, OutputMsg{Role: "usage", Usage: usage}) } if err := state.Update(msg, results); err != nil { return fmt.Errorf("step %d update: %w", step, err) } - // Stop when the model has nothing more to call. - if len(toolCalls) == 0 || step >= maxSteps-1 { - if len(toolCalls) == 0 { - if err := state.MarkComplete(); err != nil { - return fmt.Errorf("mark complete: %w", err) - } + if len(toolCalls) == 0 { + if err := state.MarkComplete(); err != nil { + return fmt.Errorf("mark complete: %w", err) } break } + if step >= maxSteps-1 { + break + } } return nil } -func (l *Loop) emit(msg OutputMsg) { - if l.cfg.Output != nil { - l.cfg.Output(msg) +func emit(cfg Config, msg OutputMsg) { + if cfg.Output != nil { + cfg.Output(msg) } } diff --git a/main.go b/main.go index e849d18..9251c06 100644 --- a/main.go +++ b/main.go @@ -570,7 +570,7 @@ func (m model) startAgent(session *agent.Session) (chan tea.Msg, context.CancelF } } - if err := agent.New(loopcfg).Run(ctx, session); err != nil { + if err := agent.Run(ctx, loopcfg, session); err != nil { select { case ch <- agentMsg{role: "error", content: err.Error()}: case <-ctx.Done():