blob: 22a646433ff79c03cda978c46bcd7e630fb14f83 [file] [log] [blame]
package com.googlesource.gerrit.plugins.chatgpt;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.chatgpt.client.chatgpt.ChatGptClient;
import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritClient;
import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritClientReview;
import com.googlesource.gerrit.plugins.chatgpt.client.patch.comment.GerritCommentRange;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptReplyItem;
import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptResponseContent;
import com.googlesource.gerrit.plugins.chatgpt.model.gerrit.GerritCodeRange;
import com.googlesource.gerrit.plugins.chatgpt.model.gerrit.GerritComment;
import com.googlesource.gerrit.plugins.chatgpt.model.review.ReviewBatch;
import com.googlesource.gerrit.plugins.chatgpt.settings.DynamicSettings;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
@Slf4j
public class PatchSetReviewer {
private static final String SPLIT_REVIEW_MSG = "Too many changes. Please consider splitting into patches smaller " +
"than %s lines for review.";
private final Gson gson = new Gson();
private final GerritClient gerritClient;
private final ChatGptClient chatGptClient;
private GerritCommentRange gerritCommentRange;
private List<ReviewBatch> reviewBatches;
private List<GerritComment> commentProperties;
private List<Integer> reviewScores;
@Inject
PatchSetReviewer(GerritClient gerritClient, ChatGptClient chatGptClient) {
this.gerritClient = gerritClient;
this.chatGptClient = chatGptClient;
}
public void review(Configuration config, GerritChange change) throws Exception {
reviewBatches = new ArrayList<>();
reviewScores = new ArrayList<>();
commentProperties = gerritClient.getClientData(change).getCommentProperties();
gerritCommentRange = new GerritCommentRange(gerritClient, change);
GerritClientReview gerritClientReview = new GerritClientReview(config);
String patchSet = gerritClient.getPatchSet(change);
if (patchSet.isEmpty()) {
log.info("No file to review has been found in the PatchSet");
return;
}
DynamicSettings.update(config, change, gerritClient);
String reviewReply = getReviewReply(config, change, patchSet);
log.debug("ChatGPT response: {}", reviewReply);
retrieveReviewBatches(reviewReply, change);
gerritClientReview.setReview(change.getFullChangeId(), reviewBatches, getReviewScore(config));
}
private void setCommentBatchMap(ReviewBatch batchMap, Integer batchID) {
if (commentProperties != null && batchID < commentProperties.size()) {
GerritComment commentProperty = commentProperties.get(batchID);
if (commentProperty != null && (commentProperty.getLine() != null || commentProperty.getRange() != null)) {
String id = commentProperty.getId();
String filename = commentProperty.getFilename();
Integer line = commentProperty.getLine();
GerritCodeRange range = commentProperty.getRange();
if (range != null) {
batchMap.setId(id);
batchMap.setFilename(filename);
batchMap.setLine(line);
batchMap.setRange(range);
}
}
}
}
private void setPatchSetReviewBatchMap(ReviewBatch batchMap, ChatGptReplyItem replyItem) {
Optional<GerritCodeRange> optGerritCommentRange = gerritCommentRange.getGerritCommentRange(replyItem);
if (optGerritCommentRange.isPresent()) {
GerritCodeRange gerritCodeRange = optGerritCommentRange.get();
batchMap.setFilename(replyItem.getFilename());
batchMap.setLine(gerritCodeRange.getStartLine());
batchMap.setRange(gerritCodeRange);
}
}
private void retrieveReviewBatches(String reviewReply, GerritChange change) {
ChatGptResponseContent reviewJson = gson.fromJson(reviewReply, ChatGptResponseContent.class);
boolean shouldFilterReplies = DynamicSettings.getInstance(change).getForcedReviewFilter();
for (ChatGptReplyItem replyItem : reviewJson.getReplies()) {
Integer score = replyItem.getScore();
if (!replyItem.isConflicting() && score != null) {
reviewScores.add(score);
}
if (shouldFilterReplies && (replyItem.isRepeated() || replyItem.isConflicting())) {
continue;
}
ReviewBatch batchMap = new ReviewBatch();
batchMap.setContent(replyItem.getReply());
if (change.getIsCommentEvent() && replyItem.getId() != null) {
setCommentBatchMap(batchMap, replyItem.getId());
}
else {
setPatchSetReviewBatchMap(batchMap, replyItem);
}
reviewBatches.add(batchMap);
}
}
private String getReviewReply(Configuration config, GerritChange change, String patchSet) throws Exception {
List<String> patchLines = Arrays.asList(patchSet.split("\n"));
if (patchLines.size() > config.getMaxReviewLines()) {
log.warn("Patch set too large. Skipping review. changeId: {}", change.getFullChangeId());
return String.format(SPLIT_REVIEW_MSG, config.getMaxReviewLines());
}
return chatGptClient.ask(config, change, patchSet);
}
private Integer getReviewScore(Configuration config) {
return config.isVotingEnabled() && !reviewScores.isEmpty() ? Collections.min(reviewScores) : null;
}
}