diff --git a/example/webui/agentpool.go b/example/webui/agentpool.go index c5e12fb..0d4ac3f 100644 --- a/example/webui/agentpool.go +++ b/example/webui/agentpool.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/json" "fmt" "os" @@ -125,16 +126,21 @@ func (a *AgentPool) List() []string { } const ( + // Connectors ConnectorTelegram = "telegram" ConnectorSlack = "slack" ConnectorDiscord = "discord" ConnectorGithubIssues = "github-issues" - ActionSearch = "search" + + // Actions + ActionSearch = "search" + ActionGithubIssueLabeler = "github-issue-labeler" + ActionGithubIssueOpener = "github-issue-opener" ) -var AvailableActions = []string{ActionSearch} +var AvailableActions = []string{ActionSearch, ActionGithubIssueLabeler, ActionGithubIssueOpener} -func (a *AgentConfig) availableActions() []Action { +func (a *AgentConfig) availableActions(ctx context.Context) []Action { actions := []Action{} for _, action := range a.Actions { @@ -150,6 +156,10 @@ func (a *AgentConfig) availableActions() []Action { switch action.Name { case ActionSearch: actions = append(actions, external.NewSearch(config)) + case ActionGithubIssueLabeler: + actions = append(actions, external.NewGithubIssueLabeler(ctx, config)) + case ActionGithubIssueOpener: + actions = append(actions, external.NewGithubIssueOpener(ctx, config)) } } @@ -204,6 +214,7 @@ func (a *AgentConfig) availableConnectors() []Connector { func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error { manager := NewManager(5) + ctx := context.Background() model := a.model if config.Model != "" { model = config.Model @@ -218,7 +229,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error fmt.Println("Model", model) fmt.Println("API URL", a.apiURL) - actions := config.availableActions() + actions := config.availableActions(ctx) stateFile, characterFile := a.stateFiles(name) @@ -226,6 +237,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error opts := []Option{ WithModel(model), WithLLMAPIURL(a.apiURL), + WithContext(ctx), WithPeriodicRuns(config.PeriodicRuns), WithPermanentGoal(config.PermanentGoal), WithActions( diff --git a/example/webui/connector/githubissue.go b/example/webui/connector/githubissue.go index 6706f15..d38bf79 100644 --- a/example/webui/connector/githubissue.go +++ b/example/webui/connector/githubissue.go @@ -86,11 +86,12 @@ func (g *GithubIssues) issuesService() { if issue.IsPullRequest() { continue } - + userName := *issue.User.Name messages := []openai.ChatCompletionMessage{ { - Role: "system", - Content: fmt.Sprintf("This is a conversation with an user that opened a Github issue with title '%s'.", issue.GetTitle()), + Role: "system", + Content: fmt.Sprintf( + `This is a conversation with an user ("%s") that opened a Github issue with title "%s" in the repository "%s" owned by "%s" .`, userName, issue.GetTitle(), g.repository, g.owner), }, { Role: "user", diff --git a/external/githubissuelabeler.go b/external/githubissuelabeler.go new file mode 100644 index 0000000..0c8ef52 --- /dev/null +++ b/external/githubissuelabeler.go @@ -0,0 +1,91 @@ +package external + +import ( + "context" + "fmt" + "strings" + + "github.com/google/go-github/v61/github" + "github.com/mudler/local-agent-framework/action" + "github.com/sashabaranov/go-openai/jsonschema" +) + +type GithubIssuesLabeler struct { + token string + availableLabels []string + context context.Context + client *github.Client +} + +func NewGithubIssueLabeler(ctx context.Context, config map[string]string) *GithubIssuesLabeler { + client := github.NewClient(nil).WithAuthToken(config["token"]) + + // Get available labels + availableLabels := []string{"bug", "enhancement"} + + if config["availableLabels"] != "" { + availableLabels = strings.Split(config["availableLabels"], ",") + } + + return &GithubIssuesLabeler{ + client: client, + token: config["token"], + context: ctx, + availableLabels: availableLabels, + } +} + +func (g *GithubIssuesLabeler) Run(params action.ActionParams) (string, error) { + result := struct { + Repository string `json:"repository"` + Owner string `json:"owner"` + Label string `json:"label"` + IssueNumber int `json:"issue_number"` + }{} + err := params.Unmarshal(&result) + if err != nil { + fmt.Printf("error: %v", err) + + return "", err + } + + labels, _, err := g.client.Issues.AddLabelsToIssue(g.context, result.Owner, result.Repository, result.IssueNumber, []string{result.Label}) + //labelsNames := []string{} + for _, l := range labels { + fmt.Println("Label added:", l.Name) + //labelsNames = append(labelsNames, l.GetName()) + } + + resultString := fmt.Sprintf("Added label '%s' to issue %d in repository %s/%s", result.Label, result.IssueNumber, result.Owner, result.Repository) + if err != nil { + resultString = fmt.Sprintf("Error adding label '%s' to issue %d in repository %s/%s: %v", result.Label, result.IssueNumber, result.Owner, result.Repository, err) + } + return resultString, err +} + +func (g *GithubIssuesLabeler) Definition() action.ActionDefinition { + return action.ActionDefinition{ + Name: "add_label_to_github_issue", + Description: "Add a label to a Github issue.", + Properties: map[string]jsonschema.Definition{ + "issue_number": { + Type: jsonschema.Number, + Description: "The number of the issue to add the label to.", + }, + "repository": { + Type: jsonschema.String, + Description: "The repository to add the label to.", + }, + "owner": { + Type: jsonschema.String, + Description: "The owner of the repository.", + }, + "label": { + Type: jsonschema.String, + Description: "The label to add to the issue.", + Enum: g.availableLabels, + }, + }, + Required: []string{"issue_number", "repository", "owner", "label"}, + } +} diff --git a/external/githubissueopener.go b/external/githubissueopener.go new file mode 100644 index 0000000..4e72600 --- /dev/null +++ b/external/githubissueopener.go @@ -0,0 +1,82 @@ +package external + +import ( + "context" + "fmt" + + "github.com/google/go-github/v61/github" + "github.com/mudler/local-agent-framework/action" + "github.com/sashabaranov/go-openai/jsonschema" +) + +type GithubIssuesOpener struct { + token string + context context.Context + client *github.Client +} + +func NewGithubIssueOpener(ctx context.Context, config map[string]string) *GithubIssuesOpener { + client := github.NewClient(nil).WithAuthToken(config["token"]) + + return &GithubIssuesOpener{ + client: client, + token: config["token"], + context: ctx, + } +} + +func (g *GithubIssuesOpener) Run(params action.ActionParams) (string, error) { + result := struct { + Title string `json:"title"` + Body string `json:"body"` + Repository string `json:"repository"` + Owner string `json:"owner"` + }{} + err := params.Unmarshal(&result) + if err != nil { + fmt.Printf("error: %v", err) + + return "", err + } + + issue := &github.IssueRequest{ + Title: &result.Title, + Body: &result.Body, + } + + resultString := "" + createdIssue, _, err := g.client.Issues.Create(g.context, result.Owner, result.Repository, issue) + if err != nil { + resultString = fmt.Sprintf("Error creating issue: %v", err) + } else { + resultString = fmt.Sprintf("Created issue %d in repository %s/%s", createdIssue.GetNumber(), result.Owner, result.Repository) + } + + return resultString, err +} + +func (g *GithubIssuesOpener) Definition() action.ActionDefinition { + return action.ActionDefinition{ + Name: "create_github_issue", + Description: "Create a new issue on a GitHub repository.", + Properties: map[string]jsonschema.Definition{ + "body": { + Type: jsonschema.String, + Description: "The number of the issue to add the label to.", + }, + "title": { + Type: jsonschema.String, + Description: "The title of the issue.", + }, + "owner": { + Type: jsonschema.String, + Description: "The owner of the repository.", + }, + "repository": { + Type: jsonschema.String, + Description: "The repository to create the issue in.", + }, + }, + Required: []string{"title", "body", "owner", "repository"}, + } +}