diff --git a/services/actions.go b/services/actions.go index 6d893d3..c0748ec 100644 --- a/services/actions.go +++ b/services/actions.go @@ -22,6 +22,8 @@ const ( ActionGithubIssueSearcher = "github-issue-searcher" ActionGithubRepositoryGet = "github-repository-get-content" ActionGithubRepositoryCreateOrUpdate = "github-repository-create-or-update-content" + ActionGithubIssueReader = "github-issue-reader" + ActionGithubIssueCommenter = "github-issue-commenter" ActionScraper = "scraper" ActionWikipedia = "wikipedia" ActionBrowse = "browse" @@ -39,6 +41,8 @@ var AvailableActions = []string{ ActionGithubIssueSearcher, ActionGithubRepositoryGet, ActionGithubRepositoryCreateOrUpdate, + ActionGithubIssueReader, + ActionGithubIssueCommenter, ActionScraper, ActionBrowse, ActionWikipedia, @@ -78,6 +82,10 @@ func Actions(a *state.AgentConfig) func(ctx context.Context) []agent.Action { allActions = append(allActions, actions.NewGithubIssueCloser(ctx, config)) case ActionGithubIssueSearcher: allActions = append(allActions, actions.NewGithubIssueSearch(ctx, config)) + case ActionGithubIssueReader: + allActions = append(allActions, actions.NewGithubIssueReader(ctx, config)) + case ActionGithubIssueCommenter: + allActions = append(allActions, actions.NewGithubIssueCommenter(ctx, config)) case ActionGithubRepositoryGet: allActions = append(allActions, actions.NewGithubRepositoryGetContent(ctx, config)) case ActionGithubRepositoryCreateOrUpdate: diff --git a/services/actions/githubissuecomment.go b/services/actions/githubissuecomment.go new file mode 100644 index 0000000..21e4181 --- /dev/null +++ b/services/actions/githubissuecomment.go @@ -0,0 +1,105 @@ +package actions + +import ( + "context" + "fmt" + + "github.com/google/go-github/v69/github" + "github.com/mudler/LocalAgent/core/action" + "github.com/sashabaranov/go-openai/jsonschema" +) + +type GithubIssuesCommenter struct { + token, repository, owner, customActionName string + context context.Context + client *github.Client +} + +func NewGithubIssueCommenter(ctx context.Context, config map[string]string) *GithubIssuesCommenter { + client := github.NewClient(nil).WithAuthToken(config["token"]) + + return &GithubIssuesCommenter{ + client: client, + token: config["token"], + customActionName: config["customActionName"], + repository: config["repository"], + owner: config["owner"], + context: ctx, + } +} + +func (g *GithubIssuesCommenter) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { + result := struct { + Repository string `json:"repository"` + Owner string `json:"owner"` + Comment string `json:"comment"` + IssueNumber int `json:"issue_number"` + }{} + err := params.Unmarshal(&result) + if err != nil { + return action.ActionResult{}, err + } + + if g.repository != "" && g.owner != "" { + result.Repository = g.repository + result.Owner = g.owner + } + + _, _, err = g.client.Issues.CreateComment(g.context, result.Owner, result.Repository, result.IssueNumber, + &github.IssueComment{ + Body: &result.Comment, + }) + resultString := fmt.Sprintf("Added comment to issue %d in repository %s/%s", result.IssueNumber, result.Owner, result.Repository) + if err != nil { + resultString = fmt.Sprintf("Error adding comment to issue %d in repository %s/%s: %v", result.IssueNumber, result.Owner, result.Repository, err) + } + return action.ActionResult{Result: resultString}, err +} + +func (g *GithubIssuesCommenter) Definition() action.ActionDefinition { + actionName := "add_comment_to_github_issue" + if g.customActionName != "" { + actionName = g.customActionName + } + description := "Add a comment to a Github issue to a repository." + if g.repository != "" && g.owner != "" { + return action.ActionDefinition{ + Name: action.ActionDefinitionName(actionName), + Description: description, + Properties: map[string]jsonschema.Definition{ + "issue_number": { + Type: jsonschema.Number, + Description: "The number of the issue to add the label to.", + }, + "comment": { + Type: jsonschema.String, + Description: "The comment to add to the issue.", + }, + }, + Required: []string{"issue_number", "comment"}, + } + } + return action.ActionDefinition{ + Name: action.ActionDefinitionName(actionName), + Description: description, + 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.", + }, + "comment": { + Type: jsonschema.String, + Description: "The comment to add to the issue.", + }, + }, + Required: []string{"issue_number", "repository", "owner", "comment"}, + } +} diff --git a/services/actions/githubissuereader.go b/services/actions/githubissuereader.go new file mode 100644 index 0000000..9d41076 --- /dev/null +++ b/services/actions/githubissuereader.go @@ -0,0 +1,99 @@ +package actions + +import ( + "context" + "fmt" + + "github.com/google/go-github/v69/github" + "github.com/mudler/LocalAgent/core/action" + "github.com/sashabaranov/go-openai/jsonschema" +) + +type GithubIssuesReader struct { + token, repository, owner, customActionName string + context context.Context + client *github.Client +} + +func NewGithubIssueReader(ctx context.Context, config map[string]string) *GithubIssuesReader { + client := github.NewClient(nil).WithAuthToken(config["token"]) + + return &GithubIssuesReader{ + client: client, + token: config["token"], + customActionName: config["customActionName"], + repository: config["repository"], + owner: config["owner"], + context: ctx, + } +} + +func (g *GithubIssuesReader) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, 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 { + return action.ActionResult{}, err + } + + if g.repository != "" && g.owner != "" { + result.Repository = g.repository + result.Owner = g.owner + } + + issue, _, err := g.client.Issues.Get(g.context, result.Owner, result.Repository, result.IssueNumber) + if err == nil && issue != nil { + return action.ActionResult{ + Result: fmt.Sprintf( + "Issue %d Repository: %s\nTitle: %s\nBody: %s", + *issue.Number, *issue.Repository.FullName, *issue.Title, *issue.Body)}, nil + } + if err != nil { + return action.ActionResult{Result: fmt.Sprintf("Error fetching issue: %s", err.Error())}, err + } + return action.ActionResult{Result: fmt.Sprintf("No issue found")}, err +} + +func (g *GithubIssuesReader) Definition() action.ActionDefinition { + actionName := "read_github_issue" + if g.customActionName != "" { + actionName = g.customActionName + } + description := "Read a Github issue." + if g.repository != "" && g.owner != "" { + return action.ActionDefinition{ + Name: action.ActionDefinitionName(actionName), + Description: description, + Properties: map[string]jsonschema.Definition{ + "issue_number": { + Type: jsonschema.Number, + Description: "The number of the issue to read.", + }, + }, + Required: []string{"issue_number"}, + } + } + return action.ActionDefinition{ + Name: action.ActionDefinitionName(actionName), + Description: description, + 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.", + }, + }, + Required: []string{"issue_number", "repository", "owner"}, + } +}