diff --git a/internal/agent/agent.go b/internal/agent/agent.go index a046c6b2d44d6f7d07300fae7ec5ac38f8ae03b8..4c9f52b123ef72ef186a2f51e50a94a0b42ab056 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -65,9 +65,10 @@ type SessionAgentCall struct { SessionID string Prompt string ProviderOptions fantasy.ProviderOptions - Attachments []message.Attachment - MaxOutputTokens int64 - Temperature *float64 + Attachments []message.Attachment + MaxOutputTokens int64 + ShowToolCalls bool + Temperature *float64 TopP *float64 TopK *int64 FrequencyPenalty *float64 @@ -357,6 +358,9 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy // TODO: implement }, OnToolCall: func(tc fantasy.ToolCallContent) error { + if call.ShowToolCalls { + slog.Default().WithGroup("TOOL").Info("call", "name", tc.ToolName, "input", tc.Input) + } toolCall := message.ToolCall{ ID: tc.ToolCallID, Name: tc.ToolName, @@ -368,6 +372,30 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy return a.messages.Update(genCtx, *currentAssistant) }, OnToolResult: func(result fantasy.ToolResultContent) error { + if call.ShowToolCalls { + content := "" + switch result.Result.GetType() { + case fantasy.ToolResultContentTypeText: + if r, ok := fantasy.AsToolResultOutputType[fantasy.ToolResultOutputContentText](result.Result); ok { + content = r.Text + } + case fantasy.ToolResultContentTypeError: + if r, ok := fantasy.AsToolResultOutputType[fantasy.ToolResultOutputContentError](result.Result); ok { + content = "Error: " + r.Error.Error() + } + case fantasy.ToolResultContentTypeMedia: + if r, ok := fantasy.AsToolResultOutputType[fantasy.ToolResultOutputContentMedia](result.Result); ok { + content = r.Text + if content == "" { + content = fmt.Sprintf("[%s content]", r.MediaType) + } + } + } + if len(content) > 200 { + content = content[:200] + "..." + } + slog.Default().WithGroup("TOOL").Info("result", "name", result.ToolName, "output", content) + } toolResult := a.convertToToolResult(result) _, createMsgErr := a.messages.Create(genCtx, currentAssistant.SessionID, message.CreateMessageParams{ Role: message.Tool, diff --git a/internal/agent/coordinator.go b/internal/agent/coordinator.go index 40076c34ee429816e93d5c5082f598a5dd02ec6c..782b6ac0da1cf26f38aa9a8ddf38a25058175d32 100644 --- a/internal/agent/coordinator.go +++ b/internal/agent/coordinator.go @@ -46,7 +46,7 @@ import ( type Coordinator interface { // INFO: (kujtim) this is not used yet we will use this when we have multiple agents // SetMainAgent(string) - Run(ctx context.Context, sessionID, prompt string, attachments ...message.Attachment) (*fantasy.AgentResult, error) + Run(ctx context.Context, sessionID, prompt string, verbose bool, attachments ...message.Attachment) (*fantasy.AgentResult, error) Cancel(sessionID string) CancelAll() IsSessionBusy(sessionID string) bool @@ -116,7 +116,7 @@ func NewCoordinator( } // Run implements Coordinator. -func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string, attachments ...message.Attachment) (*fantasy.AgentResult, error) { +func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string, verbose bool, attachments ...message.Attachment) (*fantasy.AgentResult, error) { if err := c.readyWg.Wait(); err != nil { return nil, err } @@ -163,6 +163,7 @@ func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string, Prompt: prompt, Attachments: attachments, MaxOutputTokens: maxTokens, + ShowToolCalls: verbose, ProviderOptions: mergedOptions, Temperature: temp, TopP: topP, diff --git a/internal/app/app.go b/internal/app/app.go index 3755db0d5321029b88cc1c15d8fb99911f635bf3..80fab2232baebc1ded1fb65fde313fb377b86f2f 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -145,7 +145,7 @@ func (app *App) Config() *config.Config { // RunNonInteractive runs the application in non-interactive mode with the // given prompt, printing to stdout. -func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, largeModel, smallModel string, hideSpinner bool) error { +func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, largeModel, smallModel string, hideSpinner bool, verbose bool) error { slog.Info("Running in non-interactive mode") ctx, cancel := context.WithCancel(ctx) @@ -241,7 +241,7 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt, done := make(chan response, 1) go func(ctx context.Context, sessionID, prompt string) { - result, err := app.AgentCoordinator.Run(ctx, sess.ID, prompt) + result, err := app.AgentCoordinator.Run(ctx, sess.ID, prompt, verbose) if err != nil { done <- response{ err: fmt.Errorf("failed to start agent processing stream: %w", err), diff --git a/internal/cmd/run.go b/internal/cmd/run.go index 50005a548bad0308bdca3a2afbe17503c1f86c56..e7a3e843c40b418cc42466ce5595c14e75524325 100644 --- a/internal/cmd/run.go +++ b/internal/cmd/run.go @@ -73,7 +73,7 @@ crush run --verbose "Generate a README for this project" event.SetNonInteractive(true) event.AppInitialized() - return app.RunNonInteractive(ctx, os.Stdout, prompt, largeModel, smallModel, quiet || verbose) + return app.RunNonInteractive(ctx, os.Stdout, prompt, largeModel, smallModel, quiet || verbose, verbose) }, PostRun: func(cmd *cobra.Command, args []string) { event.AppExited() diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index a19788d7bfa5b33287a604871e1fe6b7e43f2d84..8d284ed32a5a7922d910f690584e742cab816462 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -2744,7 +2744,7 @@ func (m *UI) sendMessage(content string, attachments ...message.Attachment) tea. // Capture session ID to avoid race with main goroutine updating m.session. sessionID := m.session.ID cmds = append(cmds, func() tea.Msg { - _, err := m.com.App.AgentCoordinator.Run(context.Background(), sessionID, content, attachments...) + _, err := m.com.App.AgentCoordinator.Run(context.Background(), sessionID, content, false, attachments...) if err != nil { isCancelErr := errors.Is(err, context.Canceled) isPermissionErr := errors.Is(err, permission.ErrorPermissionDenied)