feat(browseragent): add browser agent runner action (#55)

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2025-04-18 22:42:17 +02:00
committed by GitHub
parent b5a12a1da6
commit 50e56fe22f
5 changed files with 220 additions and 19 deletions

View File

@@ -18,6 +18,7 @@ const (
// Actions
ActionSearch = "search"
ActionCustom = "custom"
ActionBrowserAgentRunner = "browser-agent-runner"
ActionGithubIssueLabeler = "github-issue-labeler"
ActionGithubIssueOpener = "github-issue-opener"
ActionGithubIssueCloser = "github-issue-closer"
@@ -52,6 +53,7 @@ var AvailableActions = []string{
ActionGithubIssueSearcher,
ActionGithubRepositoryGet,
ActionGithubGetAllContent,
ActionBrowserAgentRunner,
ActionGithubRepositoryCreateOrUpdate,
ActionGithubIssueReader,
ActionGithubIssueCommenter,
@@ -71,31 +73,34 @@ var AvailableActions = []string{
ActionShellcommand,
}
func Actions(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPool) []types.Action {
return func(ctx context.Context, pool *state.AgentPool) []types.Action {
allActions := []types.Action{}
func Actions(actionsConfigs map[string]string) func(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPool) []types.Action {
return func(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPool) []types.Action {
return func(ctx context.Context, pool *state.AgentPool) []types.Action {
allActions := []types.Action{}
agentName := a.Name
agentName := a.Name
for _, a := range a.Actions {
var config map[string]string
if err := json.Unmarshal([]byte(a.Config), &config); err != nil {
xlog.Error("Error unmarshalling action config", "error", err)
continue
for _, a := range a.Actions {
var config map[string]string
if err := json.Unmarshal([]byte(a.Config), &config); err != nil {
xlog.Error("Error unmarshalling action config", "error", err)
continue
}
a, err := Action(a.Name, agentName, config, pool, actionsConfigs)
if err != nil {
continue
}
allActions = append(allActions, a)
}
a, err := Action(a.Name, agentName, config, pool)
if err != nil {
continue
}
allActions = append(allActions, a)
return allActions
}
return allActions
}
}
func Action(name, agentName string, config map[string]string, pool *state.AgentPool) (types.Action, error) {
func Action(name, agentName string, config map[string]string, pool *state.AgentPool, actionsConfigs map[string]string) (types.Action, error) {
var a types.Action
var err error
@@ -114,6 +119,8 @@ func Action(name, agentName string, config map[string]string, pool *state.AgentP
a = actions.NewGithubIssueCloser(config)
case ActionGithubIssueSearcher:
a = actions.NewGithubIssueSearch(config)
case ActionBrowserAgentRunner:
a = actions.NewBrowserAgentRunner(config, actionsConfigs["browser-agent-runner-base-url"])
case ActionGithubIssueReader:
a = actions.NewGithubIssueReader(config)
case ActionGithubPRReader:
@@ -169,6 +176,11 @@ func ActionsConfigMeta() []config.FieldGroup {
Label: "Search",
Fields: actions.SearchConfigMeta(),
},
{
Name: "browser-agent-runner",
Label: "Browser Agent Runner",
Fields: actions.BrowserAgentRunnerConfigMeta(),
},
{
Name: "generate_image",
Label: "Generate Image",

View File

@@ -0,0 +1,116 @@
package actions
import (
"context"
"fmt"
"github.com/mudler/LocalAGI/core/types"
"github.com/mudler/LocalAGI/pkg/config"
api "github.com/mudler/LocalAGI/pkg/localoperator"
"github.com/sashabaranov/go-openai/jsonschema"
)
type BrowserAgentRunner struct {
baseURL, customActionName string
client *api.Client
}
func NewBrowserAgentRunner(config map[string]string, defaultURL string) *BrowserAgentRunner {
if config["baseURL"] == "" {
config["baseURL"] = defaultURL
}
client := api.NewClient(config["baseURL"])
return &BrowserAgentRunner{
client: client,
baseURL: config["baseURL"],
customActionName: config["customActionName"],
}
}
func (b *BrowserAgentRunner) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) {
result := api.AgentRequest{}
err := params.Unmarshal(&result)
if err != nil {
return types.ActionResult{}, fmt.Errorf("failed to unmarshal params: %w", err)
}
req := api.AgentRequest{
Goal: result.Goal,
MaxAttempts: result.MaxAttempts,
MaxNoActionAttempts: result.MaxNoActionAttempts,
}
stateHistory, err := b.client.RunBrowserAgent(req)
if err != nil {
return types.ActionResult{}, fmt.Errorf("failed to run browser agent: %w", err)
}
// Format the state history into a readable string
var historyStr string
// for i, state := range stateHistory.States {
// historyStr += fmt.Sprintf("State %d:\n", i+1)
// historyStr += fmt.Sprintf(" URL: %s\n", state.CurrentURL)
// historyStr += fmt.Sprintf(" Title: %s\n", state.PageTitle)
// historyStr += fmt.Sprintf(" Description: %s\n\n", state.PageContentDescription)
// }
historyStr += fmt.Sprintf(" URL: %s\n", stateHistory.States[len(stateHistory.States)-1].CurrentURL)
historyStr += fmt.Sprintf(" Title: %s\n", stateHistory.States[len(stateHistory.States)-1].PageTitle)
historyStr += fmt.Sprintf(" Description: %s\n\n", stateHistory.States[len(stateHistory.States)-1].PageContentDescription)
return types.ActionResult{
Result: fmt.Sprintf("Browser agent completed successfully. History:\n%s", historyStr),
}, nil
}
func (b *BrowserAgentRunner) Definition() types.ActionDefinition {
actionName := "run_browser_agent"
if b.customActionName != "" {
actionName = b.customActionName
}
description := "Run a browser agent to achieve a specific goal, for example: 'Go to https://www.google.com and search for 'LocalAI', and tell me what's on the first page'"
return types.ActionDefinition{
Name: types.ActionDefinitionName(actionName),
Description: description,
Properties: map[string]jsonschema.Definition{
"goal": {
Type: jsonschema.String,
Description: "The goal for the browser agent to achieve",
},
"max_attempts": {
Type: jsonschema.Number,
Description: "Maximum number of attempts the agent can make (optional)",
},
"max_no_action_attempts": {
Type: jsonschema.Number,
Description: "Maximum number of attempts without taking an action (optional)",
},
},
Required: []string{"goal"},
}
}
func (a *BrowserAgentRunner) Plannable() bool {
return true
}
// BrowserAgentRunnerConfigMeta returns the metadata for Browser Agent Runner action configuration fields
func BrowserAgentRunnerConfigMeta() []config.Field {
return []config.Field{
{
Name: "baseURL",
Label: "Base URL",
Type: config.FieldTypeText,
Required: false,
HelpText: "Base URL of the LocalOperator API",
},
{
Name: "customActionName",
Label: "Custom Action Name",
Type: config.FieldTypeText,
HelpText: "Custom name for this action",
},
}
}