Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions pkg/agent/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ func createToolRegistry(workspace string, restrict bool, cfg *config.Config, msg
}
registry.Register(tools.NewWebFetchTool(50000))

// Browser automation tools (ActionBook)
if cfg.Tools.Browser.Enabled {
registry.Register(tools.NewBrowserSearchTool())
registry.Register(tools.NewBrowserGetTool())
registry.Register(tools.NewBrowserTool(cfg.Tools.Browser.Headless))
}

// Hardware tools (I2C, SPI) - Linux only, returns error on other platforms
registry.Register(tools.NewI2CTool())
registry.Register(tools.NewSPITool())
Expand Down Expand Up @@ -380,6 +387,32 @@ func (al *AgentLoop) runAgentLoop(ctx context.Context, opts processOptions) (str
return "", err
}

// FIX: If we used tools (iteration > 1) but got no final response, ask for a summary.
// This happens when the model thinks "Action completed" is enough and stops generating.
if finalContent == "" && iteration > 1 {
logger.InfoCF("agent", "Empty response after tool use, forcing summary", nil)

// Append a system instruction to force a response
forceSummaryMsg := providers.Message{
Role: "user",
Content: "The tools have finished execution. Please provide a concise final answer to the user's original request based on the tool outputs above.",
}
messages = append(messages, forceSummaryMsg)

// One last call without tools allowed, just for summary
resp, err := al.provider.Chat(ctx, messages, nil, al.model, map[string]interface{}{
"max_tokens": 4096,
"temperature": 0.7,
})
if err == nil && resp.Content != "" {
finalContent = resp.Content
// Update iteration count to reflect this extra step
iteration++
} else if err != nil {
logger.WarnCF("agent", "Failed to force summary: %v", map[string]interface{}{"error": err.Error()})
}
}

// If last tool had ForUser content and we already sent it, we might not need to send final response
// This is controlled by the tool's Silent flag and ForUser content

Expand Down
12 changes: 11 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,14 @@ type WebToolsConfig struct {
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"`
}

type BrowserConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_BROWSER_ENABLED"`
Headless bool `json:"headless" env:"PICOCLAW_TOOLS_BROWSER_HEADLESS"`
}

type ToolsConfig struct {
Web WebToolsConfig `json:"web"`
Web WebToolsConfig `json:"web"`
Browser BrowserConfig `json:"browser"`
}

func DefaultConfig() *Config {
Expand Down Expand Up @@ -322,6 +328,10 @@ func DefaultConfig() *Config {
MaxResults: 5,
},
},
Browser: BrowserConfig{
Enabled: true,
Headless: true,
},
},
Heartbeat: HeartbeatConfig{
Enabled: true,
Expand Down
Loading