Skip to content

Commit

Permalink
fix(gittar): optimize ai code-review (#6201) (#6212)
Browse files Browse the repository at this point in the history
* limit_sync_group: fix the issue that s.Add blocked when added-items much than maxSize.

The behaviour should be same as sync.WaitGroup: you can add all items just once at first.

* optimize prompt and review result

* replace function-call by normal chat completion

* remove useless code

Co-authored-by: sfwn <[email protected]>
  • Loading branch information
erda-bot and sfwn authored Jan 3, 2024
1 parent 368f45d commit ab37ef6
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 180 deletions.
12 changes: 12 additions & 0 deletions cmd/gittar/conf/i18n/i18n.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
zh:
mr.note.comment.cannot.be.empty: 评论不能为空
template.mr.ai.cr.tip.reach.max.limit: 这里仅展示前 %d 个文件的审查结果。对于其他未审查到的文件,请移至 "**变更**" 页里,手动点击 "**AI 审查**" 按钮,或者手动选择代码片段开启 "**AI 对话**"。
mr.ai.cr.title: AI 代码审查
file: 文件
snippet: 代码片段
mr.ai.cr.no.suggestion: 你的代码变更看起来不错,暂无审查建议。
template.mr.ai.cr.file.content.max.limit: 文件内容超长,本次审查至第 %d 行。对于其他未审查到的内容,请在 "**变更**" 页里手动选择代码片段开启 "**AI 对话**"。
en:
mr.note.comment.cannot.be.empty: Comment cannot be empty
template.mr.ai.cr.tip.reach.max.limit: Only the first %d files are displayed here. For other files that have not been reviewed, please manually click the "**AI Code Review**" button in the "**Changes**" tab, or manually select the code snippet to start the "**AI Conversation**".
mr.ai.cr.title: AI Code Review
file: File
snippet: Code Snippet
mr.ai.cr.no.suggestion: Your code changes look good, no review suggestions for now.
template.mr.ai.cr.file.content.max.limit: The file content is too long, only the first %d lines are reviewed. For other content that has not been reviewed, please manually select the code snippet in "Changes" tab to start the "AI Conversation".
48 changes: 39 additions & 9 deletions internal/tools/gittar/ai/cr/impl/cr_mr/mr.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,26 @@
package cr_mr

import (
"fmt"
"strings"
"sync"

"github.com/sirupsen/logrus"

"github.com/erda-project/erda-infra/providers/i18n"
"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/internal/tools/gittar/ai/cr/impl/cr_mr_file"
"github.com/erda-project/erda/internal/tools/gittar/ai/cr/util/mdutil"
"github.com/erda-project/erda/internal/tools/gittar/ai/cr/util/mrutil"
"github.com/erda-project/erda/internal/tools/gittar/models"
"github.com/erda-project/erda/internal/tools/gittar/pkg/gitmodule"
"github.com/erda-project/erda/pkg/limit_sync_group"
)

const (
MaxDiffFileNum = 5
)

type mrReviewer struct {
req models.AICodeReviewNoteRequest
repo *gitmodule.Repository
Expand All @@ -38,26 +48,32 @@ func init() {
})
}

func (r *mrReviewer) CodeReview() string {
func (r *mrReviewer) CodeReview(i18n i18n.Translator, lang i18n.LanguageCodes) string {
diff := mrutil.GetDiffFromMR(r.repo, r.mr)

// mr has many changed files, we will review only the first ten files one by one. Then, combine the file-level suggestions.

var changedFiles []models.FileCodeReviewer
var reachMaxDiffFileNumLimit bool
for _, diffFile := range diff.Files {
if len(changedFiles) >= 10 {
if len(changedFiles) >= MaxDiffFileNum {
reachMaxDiffFileNumLimit = true
break
}
if len(diffFile.Sections) > 0 {
fr := cr_mr_file.NewFileReviewer(diffFile, r.user, r.mr)
fr, err := cr_mr_file.NewFileReviewer(diffFile.Name, r.repo, r.mr, r.user)
if err != nil {
logrus.Warnf("failed to create file reviewer for file %s, err: %v", diffFile.Name, err)
continue
}
changedFiles = append(changedFiles, fr)
}
}

// parallel do file-level cr
var fileOrder []string
fileSuggestions := make(map[string]string)
wg := limit_sync_group.NewSemaphore(5) // parallel is 5
wg := limit_sync_group.NewSemaphore(MaxDiffFileNum) // parallel is 5
var mu sync.Mutex

wg.Add(len(changedFiles))
Expand All @@ -66,19 +82,33 @@ func (r *mrReviewer) CodeReview() string {
go func(file models.FileCodeReviewer) {
defer wg.Done()

fileSuggestion := file.CodeReview(i18n, lang)
if strings.TrimSpace(fileSuggestion) == "" {
fileSuggestion = i18n.Text(lang, models.I18nKeyMrAICrNoSuggestion)
}
mu.Lock()
fileSuggestions[file.GetFileName()] = file.CodeReview()
fileSuggestions[file.GetFileName()] = fileSuggestion
mu.Unlock()
}(file)
}
wg.Wait()

// combine result
var mrReviewResult string
mrReviewResult = "# AI Code Review\n"
var mrReviewResults []string
mrReviewResults = append(mrReviewResults, fmt.Sprintf("# %s", i18n.Text(lang, models.I18nKeyMrAICrTitle)))
if reachMaxDiffFileNumLimit {
tip := fmt.Sprintf(i18n.Text(lang, models.I18nKeyTemplateMrAICrTipForEachMaxLimit), MaxDiffFileNum)
tip = mdutil.MakeRef(mdutil.MakeItalic(tip))
mrReviewResults = append(mrReviewResults, tip)
}
mrReviewResults = append(mrReviewResults, "")
for _, fileName := range fileOrder {
mrReviewResult += "------\n## File: `" + fileName + "`\n\n" + fileSuggestions[fileName] + "\n"
mrReviewResults = append(mrReviewResults, "------")
mrReviewResults = append(mrReviewResults, fmt.Sprintf("## %s: `%s`", i18n.Text(lang, models.I18nKeyFile), fileName))
mrReviewResults = append(mrReviewResults, "")
mrReviewResults = append(mrReviewResults, fileSuggestions[fileName])
mrReviewResults = append(mrReviewResults, "")
}

return mrReviewResult
return strings.Join(mrReviewResults, "\n")
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
messages:
- role: system
content: Please reply in {{.UserLang}}.

- role: system
content: |
Please give a review suggestions for the selected code, use markdown title for each suggestion. Code examples can be provided when necessary.
The first-level title is: AI Code Review.
Code:
{{.SelectedCode}}
- role: system
content: Please reply in Chinese.
12 changes: 8 additions & 4 deletions internal/tools/gittar/ai/cr/impl/cr_mr_code_snippet/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import (
"github.com/sashabaranov/go-openai"
"gopkg.in/yaml.v3"

"github.com/erda-project/erda-infra/providers/i18n"
"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/internal/tools/gittar/ai/cr/util/aiutil"
"github.com/erda-project/erda/internal/tools/gittar/ai/cr/util/i18nutil"
"github.com/erda-project/erda/internal/tools/gittar/models"
"github.com/erda-project/erda/internal/tools/gittar/pkg/gitmodule"
)
Expand All @@ -41,6 +43,7 @@ type CodeSnippet struct {
CodeLanguage string
SelectedCode string
Truncated bool // if there are too many changes, we have to truncate the content according to the model context
UserLang string

user *models.User
}
Expand Down Expand Up @@ -82,15 +85,16 @@ func newSnippetCodeReviewer(codeLang, selectedCode string, truncated bool, user
return cs
}

func (cs CodeSnippet) CodeReview() string {
// invoke ai
req := cs.constructAIRequest()
func (cs CodeSnippet) CodeReview(i18n i18n.Translator, lang i18n.LanguageCodes) string {
// construct AI request
req := cs.constructAIRequest(i18n, lang)

// invoke
return aiutil.InvokeAI(req, cs.user)
}

func (cs CodeSnippet) constructAIRequest() openai.ChatCompletionRequest {
func (cs CodeSnippet) constructAIRequest(i18n i18n.Translator, lang i18n.LanguageCodes) openai.ChatCompletionRequest {
cs.UserLang = i18nutil.GetUserLang(lang)
msgs := deepcopy.Copy(promptStruct.Messages).([]openai.ChatCompletionMessage)

// invoke ai
Expand Down
27 changes: 0 additions & 27 deletions internal/tools/gittar/ai/cr/impl/cr_mr_file/fc.yaml

This file was deleted.

Loading

0 comments on commit ab37ef6

Please sign in to comment.