Handle duplicate and conflicting comments
ChatGPT comments that are either duplicates of or contradict existing
comments will not be shown.
Jira-Id: IT-103
Change-Id: Iac6fa1fcb76a2e0cae8bb6ced558e32a524e34e8
Signed-off-by: Patrizio <patrizio.gelosi@amarulasolutions.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/PatchSetReviewer.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/PatchSetReviewer.java
index 6356ec4..a7e02e8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/PatchSetReviewer.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/PatchSetReviewer.java
@@ -87,6 +87,9 @@
private void retrieveReviewBatches(ChatGptResponseContent reviewJson, GerritChange change) {
for (ChatGptReplyItem replyItem : reviewJson.getReplies()) {
+ if (replyItem.isRepeated() || replyItem.isConflicting()) {
+ continue;
+ }
ReviewBatch batchMap = new ReviewBatch();
batchMap.setContent(replyItem.getReply());
if (change.getIsCommentEvent() && replyItem.getId() != null) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ClientCommands.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ClientCommands.java
index dcde864..262c3fb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ClientCommands.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ClientCommands.java
@@ -49,4 +49,9 @@
return false;
}
+ public static String removeCommands(String comment) {
+ Matcher reviewCommandMatcher = COMMAND_PATTERN.matcher(comment);
+ return reviewCommandMatcher.replaceAll("");
+ }
+
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptClient.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptClient.java
index 84437f9..194c6b5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptClient.java
@@ -114,7 +114,7 @@
}
private String createRequestBody(Configuration config, String changeId, String patchSet) {
- ChatGptPrompt chatGptPrompt = new ChatGptPrompt(config);
+ ChatGptPrompt chatGptPrompt = new ChatGptPrompt(config, isCommentEvent);
ChatGptRequest.Message systemMessage = ChatGptRequest.Message.builder()
.role("system")
.content(chatGptPrompt.getGptSystemPrompt())
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptTools.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptTools.java
index eee3513..e7c8a6a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptTools.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/chatgpt/ChatGptTools.java
@@ -1,5 +1,6 @@
package com.googlesource.gerrit.plugins.chatgpt.client.chatgpt;
+import com.google.gson.Gson;
import com.googlesource.gerrit.plugins.chatgpt.client.common.ClientBase;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptRequest;
@@ -11,6 +12,7 @@
class ChatGptTools extends ClientBase {
private final boolean isCommentEvent;
+ private final Gson gson = new Gson();
public ChatGptTools(Configuration config, Boolean isCommentEvent) {
super(config);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientBase.java
index aa71318..942657f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientBase.java
@@ -1,10 +1,8 @@
package com.googlesource.gerrit.plugins.chatgpt.client.common;
-import com.google.gson.Gson;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
public abstract class ClientBase {
- protected final Gson gson = new Gson();
protected Configuration config;
public ClientBase(Configuration config) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientMessage.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientMessage.java
index bff7d56..a1a4b76 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientMessage.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/common/ClientMessage.java
@@ -1,6 +1,8 @@
package com.googlesource.gerrit.plugins.chatgpt.client.common;
+import com.googlesource.gerrit.plugins.chatgpt.client.ClientCommands;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.regex.Matcher;
@@ -13,11 +15,19 @@
private final Pattern botMentionPattern;
+ @Getter
+ private String message;
+
public ClientMessage(Configuration config) {
super(config);
botMentionPattern = getBotMentionPattern();
}
+ public ClientMessage(Configuration config, String message) {
+ this(config);
+ this.message = message;
+ }
+
public boolean isBotAddressed(String message) {
log.debug("Processing comment: {}", message);
Matcher userMatcher = botMentionPattern.matcher(message);
@@ -30,12 +40,19 @@
return true;
}
- protected String removeHeadings(String message) {
- return MESSAGE_HEADING_PATTERN.matcher(message).replaceAll("");
+ public ClientMessage removeHeadings() {
+ message = MESSAGE_HEADING_PATTERN.matcher(message).replaceAll("");
+ return this;
}
- protected String removeMentions(String message) {
- return botMentionPattern.matcher(message).replaceAll("").trim();
+ public ClientMessage removeMentions() {
+ message = botMentionPattern.matcher(message).replaceAll("").trim();
+ return this;
+ }
+
+ public ClientMessage removeCommands() {
+ message = ClientCommands.removeCommands(message);
+ return this;
}
private Pattern getBotMentionPattern() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClient.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClient.java
index a46cab5..fe08aab 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClient.java
@@ -67,6 +67,11 @@
return gerritClientFacade.retrieveLastComments(change);
}
+ public void retrieveAllComments(GerritChange change) {
+ updateGerritClientFacade(change);
+ gerritClientFacade.retrieveAllComments(change);
+ }
+
public GerritClientData getClientData(GerritChange change) {
updateGerritClientFacade(change);
return gerritClientFacade.getClientData(change);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientBase.java
index 108a8cc..3e2c505 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientBase.java
@@ -1,6 +1,7 @@
package com.googlesource.gerrit.plugins.chatgpt.client.gerrit;
import com.google.common.net.HttpHeaders;
+import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.googlesource.gerrit.plugins.chatgpt.client.common.ClientBase;
@@ -23,6 +24,7 @@
@Slf4j
public abstract class GerritClientBase extends ClientBase {
protected final HttpClientWithRetry httpClientWithRetry = new HttpClientWithRetry();
+ protected final Gson gson = new Gson();
@Getter
protected HashMap<String, FileDiffProcessed> fileDiffsProcessed = new HashMap<>();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientComments.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientComments.java
index 8eb3fef..4e702fe 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientComments.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientComments.java
@@ -25,7 +25,6 @@
private final HashMap<String, GerritComment> commentMap;
private final HashMap<String, GerritComment> commentGlobalMap;
- private ClientMessage clientMessage;
private String authorUsername;
@Getter
private List<GerritComment> commentProperties;
@@ -42,7 +41,6 @@
}
public boolean retrieveLastComments(GerritChange change) {
- clientMessage = new ClientMessage(config);
CommentAddedEvent commentAddedEvent = (CommentAddedEvent) change.getEvent();
authorUsername = commentAddedEvent.author.get().username;
log.debug("Found comments by '{}' on {}", authorUsername, change.getEventTimeStamp());
@@ -54,12 +52,20 @@
log.info("Review of comments from user '{}' is disabled.", authorUsername);
return false;
}
- addAllComments(change);
+ addLastComments(change);
return !commentProperties.isEmpty();
}
- private List<GerritComment> getLastComments(GerritChange change) throws Exception {
+ public void retrieveAllComments(GerritChange change) {
+ try {
+ retrieveComments(change);
+ } catch (Exception e) {
+ log.error("Error while retrieving all comments for change: {}", change.getFullChangeId(), e);
+ }
+ }
+
+ private List<GerritComment> retrieveComments(GerritChange change) throws Exception {
URI uri = URI.create(config.getGerritAuthBaseUrl()
+ UriResourceLocator.gerritGetAllPatchSetCommentsUri(change.getFullChangeId()));
String responseBody = forwardGetRequest(uri);
@@ -97,9 +103,10 @@
return latestComments.getOrDefault(latestChangeMessageId, null);
}
- private void addAllComments(GerritChange change) {
+ private void addLastComments(GerritChange change) {
+ ClientMessage clientMessage = new ClientMessage(config);
try {
- List<GerritComment> latestComments = getLastComments(change);
+ List<GerritComment> latestComments = retrieveComments(change);
if (latestComments == null) {
return;
}
@@ -114,7 +121,7 @@
}
}
} catch (Exception e) {
- log.error("Error while retrieving comments for change: {}", change.getFullChangeId(), e);
+ log.error("Error while retrieving last comments for change: {}", change.getFullChangeId(), e);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientFacade.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientFacade.java
index 1443f82..6216474 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientFacade.java
@@ -52,6 +52,10 @@
return gerritClientComments.retrieveLastComments(change);
}
+ public void retrieveAllComments(GerritChange change) {
+ gerritClientComments.retrieveAllComments(change);
+ }
+
public GerritClientData getClientData(GerritChange change) {
return new GerritClientData(
gerritClientPatchSet.getFileDiffsProcessed(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientReview.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientReview.java
index cb4bb7a..e9c510b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/gerrit/GerritClientReview.java
@@ -23,6 +23,7 @@
@Slf4j
public class GerritClientReview extends GerritClientAccount {
private static final String BULLET_POINT = "* ";
+ private static final String EMPTY_REVIEW_MESSAGE = "No review update for this Change Set";
public GerritClientReview(Configuration config) {
super(config);
@@ -97,6 +98,9 @@
if (!comments.isEmpty()) {
reviewMap.setComments(comments);
}
+ if (messages.isEmpty() && comments.isEmpty()) {
+ reviewMap.setMessage(EMPTY_REVIEW_MESSAGE);
+ }
if (reviewScore != null) {
reviewMap.setLabels(new GerritReview.Labels(reviewScore));
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/code/InlineCode.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/code/InlineCode.java
index b3f5ea6..4cd9d28 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/code/InlineCode.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/code/InlineCode.java
@@ -10,6 +10,8 @@
import java.util.List;
import java.util.Optional;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.StringUtils.joinWithNewLine;
+
@Slf4j
public class InlineCode {
private final CodeFinder codeFinder;
@@ -28,7 +30,7 @@
for (int line_num = range.startLine; line_num <= range.endLine; line_num++) {
codeByRange.add(getLineSlice(line_num));
}
- return String.join("\n", codeByRange);
+ return joinWithNewLine(codeByRange);
}
else {
return newContent.get(commentProperty.getLine());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/diff/FileDiffProcessed.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/diff/FileDiffProcessed.java
index 50931a6..9bfeb8a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/diff/FileDiffProcessed.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/patch/diff/FileDiffProcessed.java
@@ -14,6 +14,8 @@
import java.util.List;
import java.util.TreeMap;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.StringUtils.joinWithNewLine;
+
@Slf4j
public class FileDiffProcessed {
private static final int MIN_RANDOM_PLACEHOLDER_VARIABLE_LENGTH = 1;
@@ -88,7 +90,7 @@
private void updateCodeEntities(Field diffField, List<String> diffLines) throws IllegalAccessException {
String diffType = diffField.getName();
- String content = String.join("\n", diffLines);
+ String content = joinWithNewLine(diffLines);
diffField.set(diffContentItem, content);
// If the lines modified in the PatchSet are not deleted, they are utilized to populate newContent and
// charToLineMapItem
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptComment.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptComment.java
index e081861..7b9d05e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptComment.java
@@ -1,5 +1,6 @@
package com.googlesource.gerrit.plugins.chatgpt.client.prompt;
+import com.googlesource.gerrit.plugins.chatgpt.client.common.ClientBase;
import com.googlesource.gerrit.plugins.chatgpt.client.common.ClientMessage;
import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
@@ -8,7 +9,7 @@
import lombok.extern.slf4j.Slf4j;
@Slf4j
-public class ChatGptComment extends ClientMessage {
+public class ChatGptComment extends ClientBase {
private final Integer gptAccountId;
public ChatGptComment(Configuration config, GerritChange change) {
@@ -17,17 +18,11 @@
}
protected String getCleanedMessage(GerritComment commentProperty) {
- String commentMessage = commentProperty.getMessage();
- if (isFromAssistant(commentProperty)) {
- return removeHeadings(commentMessage);
+ ClientMessage commentMessage = new ClientMessage(config, commentProperty.getMessage());
+ if (!isFromAssistant(commentProperty)) {
+ commentMessage.removeCommands().removeMentions();
}
- else {
- return removeHeadings(removeMentions(commentMessage));
- }
- }
-
- protected String getMessageWithoutMentions(GerritComment commentProperty) {
- return removeMentions(commentProperty.getMessage());
+ return commentMessage.removeHeadings().getMessage();
}
protected boolean isFromAssistant(GerritComment commentProperty) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptHistory.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptHistory.java
index bb05558..70fda1f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptHistory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptHistory.java
@@ -1,5 +1,6 @@
package com.googlesource.gerrit.plugins.chatgpt.client.prompt;
+import com.google.gson.Gson;
import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptRequest;
@@ -20,6 +21,7 @@
private static final String ROLE_USER = "user";
private static final String ROLE_ASSISTANT = "assistant";
+ private final Gson gson = new Gson();
private final HashMap<String, GerritComment> commentMap;
private final HashMap<String, GerritComment> commentGlobalMap;
private final List<GerritComment> detailComments;
@@ -33,14 +35,11 @@
}
public String retrieveHistory(GerritComment commentProperty) {
- if (commentProperty.getInReplyTo() != null) {
- return retrieveMessageHistory(commentProperty);
- }
- else if (commentProperty.getFilename().equals(GLOBAL_MESSAGES_FILENAME) && detailComments != null) {
+ if (commentProperty.getFilename().equals(GLOBAL_MESSAGES_FILENAME) && detailComments != null) {
return retrieveGlobalMessageHistory();
}
else {
- return getMessageWithoutMentions(commentProperty);
+ return retrieveMessageHistory(commentProperty);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptPrompt.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptPrompt.java
index 4b9a52a..eca51e4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptPrompt.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptPrompt.java
@@ -6,88 +6,145 @@
import com.googlesource.gerrit.plugins.chatgpt.model.settings.Settings;
import com.googlesource.gerrit.plugins.chatgpt.settings.DynamicSettings;
import com.googlesource.gerrit.plugins.chatgpt.utils.FileUtils;
+import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.googlesource.gerrit.plugins.chatgpt.utils.StringUtils.*;
@Slf4j
public class ChatGptPrompt {
public static final String SPACE = " ";
- public static final String DOT_SPACE = ". ";
+ public static final String COMMA = ", ";
+ public static final String SEMICOLON = "; ";
+ public static final String DOT = ". ";
+ public static final String BACKTICK = "`";
+ public static final String[] PATCH_SET_REVIEW_REPLIES_ATTRIBUTES = {"reply", "repeated", "conflicting"};
+ public static final String[] REQUEST_REPLIES_ATTRIBUTES = {"reply", "id", "changeId"};
// Prompt constants loaded from JSON file
public static String DEFAULT_GPT_SYSTEM_PROMPT;
- public static String DEFAULT_GPT_SYSTEM_PROMPT_INSTRUCTIONS;
- public static String DEFAULT_GPT_REVIEW_USER_PROMPT;
- public static String DEFAULT_GPT_JSON_USER_PROMPT;
- public static String DEFAULT_GPT_REQUEST_JSON_USER_PROMPT;
- public static String DEFAULT_GPT_JSON_USER_PROMPT_2;
- public static String DEFAULT_GPT_JSON_USER_PROMPT_ENFORCE_RESPONSE_CHECK;
- public static String DEFAULT_GPT_REQUEST_USER_PROMPT_1;
- public static String DEFAULT_GPT_REQUEST_USER_PROMPT_2;
- public static String DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT;
- public static String DEFAULT_GPT_VOTING_REVIEW_USER_PROMPT;
+ public static String DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION;
+ public static String DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW;
+ public static String DEFAULT_GPT_REVIEW_PROMPT;
+ public static String DEFAULT_GPT_REVIEW_PROMPT_REVIEW;
+ public static String DEFAULT_GPT_REVIEW_PROMPT_MESSAGE_HISTORY;
+ public static String DEFAULT_GPT_REVIEW_PROMPT_DIFF;
+ public static String DEFAULT_GPT_REPLIES_PROMPT;
+ public static String DEFAULT_GPT_REPLIES_PROMPT_INLINE;
+ public static String DEFAULT_GPT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK;
+ public static String DEFAULT_GPT_REQUEST_PROMPT_DIFF;
+ public static String DEFAULT_GPT_REQUEST_PROMPT_REQUESTS;
+ public static String DEFAULT_GPT_REVIEW_PROMPT_COMMIT_MESSAGES;
+ public static String DEFAULT_GPT_REVIEW_PROMPT_VOTING;
+ public static Map<String, String> DEFAULT_GPT_REPLIES_ATTRIBUTES;
private final Configuration config;
+ @Setter
+ private boolean isCommentEvent;
public ChatGptPrompt(Configuration config) {
this.config = config;
loadPrompts();
}
- public static String getDefaultSystemPrompt() {
- return DEFAULT_GPT_SYSTEM_PROMPT + DOT_SPACE + DEFAULT_GPT_SYSTEM_PROMPT_INSTRUCTIONS;
+ public ChatGptPrompt(Configuration config, boolean isCommentEvent) {
+ this(config);
+ this.isCommentEvent = isCommentEvent;
+ }
+
+ public static String getDefaultGptReviewSystemPrompt() {
+ return DEFAULT_GPT_SYSTEM_PROMPT + DOT +
+ DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION + SPACE +
+ DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW;
}
public static String getPatchSetReviewUserPrompt() {
- return DEFAULT_GPT_JSON_USER_PROMPT + DOT_SPACE + DEFAULT_GPT_JSON_USER_PROMPT_2;
+ return buildFieldSpecifications(PATCH_SET_REVIEW_REPLIES_ATTRIBUTES) + SPACE +
+ DEFAULT_GPT_REPLIES_PROMPT_INLINE;
}
public static String getCommentRequestUserPrompt(int commentPropertiesSize) {
- return DEFAULT_GPT_JSON_USER_PROMPT + SPACE +
- DEFAULT_GPT_REQUEST_JSON_USER_PROMPT + DOT_SPACE +
- DEFAULT_GPT_JSON_USER_PROMPT_2 + SPACE +
- String.format(DEFAULT_GPT_JSON_USER_PROMPT_ENFORCE_RESPONSE_CHECK, commentPropertiesSize);
+ return buildFieldSpecifications(REQUEST_REPLIES_ATTRIBUTES) + SPACE +
+ DEFAULT_GPT_REPLIES_PROMPT_INLINE + SPACE +
+ String.format(DEFAULT_GPT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK, commentPropertiesSize);
}
public String getGptSystemPrompt() {
- return config.getString(Configuration.KEY_GPT_SYSTEM_PROMPT, DEFAULT_GPT_SYSTEM_PROMPT) + DOT_SPACE +
- DEFAULT_GPT_SYSTEM_PROMPT_INSTRUCTIONS;
+ List<String> prompt = new ArrayList<>(Arrays.asList(
+ config.getString(Configuration.KEY_GPT_SYSTEM_PROMPT, DEFAULT_GPT_SYSTEM_PROMPT), DOT,
+ DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION
+ ));
+ if (!isCommentEvent) {
+ prompt.addAll(Arrays.asList(SPACE, DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW));
+ }
+ return concatenate(prompt);
}
public String getGptUserPrompt(String patchSet, String changeId) {
List<String> prompt = new ArrayList<>();
Settings settings = DynamicSettings.getInstance(changeId);
String gptRequestUserPrompt = settings.getGptRequestUserPrompt();
- if (gptRequestUserPrompt != null && !gptRequestUserPrompt.isEmpty()) {
+ boolean isValidRequestUserPrompt = gptRequestUserPrompt != null && !gptRequestUserPrompt.isEmpty();
+ if (isCommentEvent && isValidRequestUserPrompt) {
log.debug("ConfigsDynamically value found: {}", gptRequestUserPrompt);
prompt.addAll(Arrays.asList(
- DEFAULT_GPT_REQUEST_USER_PROMPT_1,
+ DEFAULT_GPT_REQUEST_PROMPT_DIFF,
patchSet,
- DEFAULT_GPT_REQUEST_USER_PROMPT_2,
+ DEFAULT_GPT_REQUEST_PROMPT_REQUESTS,
gptRequestUserPrompt,
getCommentRequestUserPrompt(settings.getCommentPropertiesSize())
));
}
else {
- prompt.add(DEFAULT_GPT_REVIEW_USER_PROMPT);
- prompt.add(getPatchSetReviewUserPrompt());
- if (config.getGptReviewCommitMessages()) {
- prompt.add(DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT);
- }
- if (config.isVotingEnabled()) {
- prompt.add(String.format(DEFAULT_GPT_VOTING_REVIEW_USER_PROMPT, config.getVotingMinScore(),
- config.getVotingMaxScore()));
- }
+ prompt.add(DEFAULT_GPT_REVIEW_PROMPT);
+ prompt.addAll(getReviewSteps());
+ prompt.add(DEFAULT_GPT_REVIEW_PROMPT_DIFF);
prompt.add(patchSet);
+ if (isValidRequestUserPrompt) {
+ prompt.add(DEFAULT_GPT_REVIEW_PROMPT_MESSAGE_HISTORY);
+ prompt.add(gptRequestUserPrompt);
+ }
}
- return String.join("\n", prompt);
+ return joinWithNewLine(prompt);
+ }
+
+ private static String buildFieldSpecifications(String[] filterFields) {
+ Set<String> orderedFilterFields = new LinkedHashSet<>(Arrays.asList(filterFields));
+ Map<String, String> attributes = DEFAULT_GPT_REPLIES_ATTRIBUTES.entrySet().stream()
+ .filter(entry -> orderedFilterFields.contains(entry.getKey()))
+ .collect(Collectors.toMap(
+ entry -> BACKTICK + entry.getKey() + BACKTICK,
+ Map.Entry::getValue,
+ (oldValue, newValue) -> oldValue,
+ LinkedHashMap::new
+ ));
+ List<String> fieldDescription = attributes.entrySet().stream()
+ .map(entry -> entry.getKey() + SPACE + entry.getValue())
+ .collect(Collectors.toList());
+
+ return String.format(DEFAULT_GPT_REPLIES_PROMPT,
+ String.join(COMMA, attributes.keySet()),
+ String.join(SEMICOLON, fieldDescription)
+ );
+ }
+
+ private List<String> getReviewSteps() {
+ List<String> steps = new ArrayList<>(){};
+ steps.add(DEFAULT_GPT_REVIEW_PROMPT_REVIEW + SPACE + getPatchSetReviewUserPrompt());
+ if (config.getGptReviewCommitMessages()) {
+ steps.add(DEFAULT_GPT_REVIEW_PROMPT_COMMIT_MESSAGES);
+ }
+ if (config.isVotingEnabled()) {
+ steps.add(String.format(DEFAULT_GPT_REVIEW_PROMPT_VOTING, config.getVotingMinScore(),
+ config.getVotingMaxScore()));
+ }
+ return getNumberedList(steps);
}
private void loadPrompts() {
@@ -96,8 +153,8 @@
Gson gson = new Gson();
Class<? extends ChatGptPrompt> me = this.getClass();
try (InputStreamReader reader = FileUtils.getInputStreamReader("Config/prompts.json")) {
- Map<String, String> values = gson.fromJson(reader, new TypeToken<Map<String, String>>(){}.getType());
- for (Map.Entry<String, String> entry : values.entrySet()) {
+ Map<String, Object> values = gson.fromJson(reader, new TypeToken<Map<String, Object>>(){}.getType());
+ for (Map.Entry<String, Object> entry : values.entrySet()) {
try {
Field field = me.getDeclaredField(entry.getKey());
field.setAccessible(true);
@@ -110,6 +167,8 @@
} catch (IOException e) {
throw new RuntimeException("Failed to load prompts", e);
}
+ // Keep the given order of attributes
+ DEFAULT_GPT_REPLIES_ATTRIBUTES = new LinkedHashMap<>(DEFAULT_GPT_REPLIES_ATTRIBUTES);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPrompt.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPrompt.java
index 7ba9056..8444029 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPrompt.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPrompt.java
@@ -1,61 +1,34 @@
package com.googlesource.gerrit.plugins.chatgpt.client.prompt;
-import com.googlesource.gerrit.plugins.chatgpt.client.common.ClientBase;
+import com.google.gson.Gson;
import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
-import com.googlesource.gerrit.plugins.chatgpt.client.patch.code.InlineCode;
-import com.googlesource.gerrit.plugins.chatgpt.client.patch.diff.FileDiffProcessed;
import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
-import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptRequestItem;
-import com.googlesource.gerrit.plugins.chatgpt.model.common.CommentData;
-import com.googlesource.gerrit.plugins.chatgpt.model.gerrit.GerritComment;
+import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptHistoryItem;
import com.googlesource.gerrit.plugins.chatgpt.model.common.GerritClientData;
-import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
@Slf4j
-public class ChatGptUserPrompt extends ClientBase {
- private final GerritClientData gerritClientData;
- private final HashMap<String, FileDiffProcessed> fileDiffsProcessed;
+public class ChatGptUserPrompt {
+ private final ChatGptUserPromptBase chatGptUserPromptBase;
+ private final Gson gson = new Gson();
- private ChatGptHistory gptMessageHistory;
- @Getter
- private List<GerritComment> commentProperties;
-
- public ChatGptUserPrompt(Configuration config, GerritClientData gerritClientData) {
- super(config);
- this.gerritClientData = gerritClientData;
- fileDiffsProcessed = gerritClientData.getFileDiffsProcessed();
- CommentData commentData = gerritClientData.getCommentData();
- commentProperties = commentData.getCommentProperties();
+ public ChatGptUserPrompt(Configuration config, GerritChange change, GerritClientData gerritClientData) {
+ if (change.getIsCommentEvent()) {
+ chatGptUserPromptBase = new ChatGptUserPromptRequests(config, change, gerritClientData);
+ }
+ else {
+ chatGptUserPromptBase = new ChatGptUserPromptReview(config, change, gerritClientData);
+ }
}
- public String buildPrompt(GerritChange change) {
- gptMessageHistory = new ChatGptHistory(config, change, gerritClientData);
- List<ChatGptRequestItem> requestItems = new ArrayList<>();
- for (int i = 0; i < commentProperties.size(); i++) {
- requestItems.add(getRequestItem(i));
+ public String buildPrompt() {
+ for (int i = 0; i < chatGptUserPromptBase.getCommentProperties().size(); i++) {
+ chatGptUserPromptBase.addHistoryItem(i);
}
- return requestItems.isEmpty() ? "" : gson.toJson(requestItems);
- }
-
- private ChatGptRequestItem getRequestItem(int i) {
- ChatGptRequestItem requestItem = new ChatGptRequestItem();
- GerritComment commentProperty = commentProperties.get(i);
- requestItem.setId(i);
- if (commentProperty.getLine() != null || commentProperty.getRange() != null) {
- String filename = commentProperty.getFilename();
- InlineCode inlineCode = new InlineCode(fileDiffsProcessed.get(filename));
- requestItem.setFilename(filename);
- requestItem.setLineNumber(commentProperty.getLine());
- requestItem.setCodeSnippet(inlineCode.getInlineCode(commentProperty));
- }
- requestItem.setRequest(gptMessageHistory.retrieveHistory(commentProperty));
-
- return requestItem;
+ List<ChatGptHistoryItem> historyItems = chatGptUserPromptBase.getHistoryItems();
+ return historyItems.isEmpty() ? "" : gson.toJson(historyItems);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptBase.java
new file mode 100644
index 0000000..29b3c88
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptBase.java
@@ -0,0 +1,54 @@
+package com.googlesource.gerrit.plugins.chatgpt.client.prompt;
+
+import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.chatgpt.client.patch.code.InlineCode;
+import com.googlesource.gerrit.plugins.chatgpt.client.patch.diff.FileDiffProcessed;
+import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptHistoryItem;
+import com.googlesource.gerrit.plugins.chatgpt.model.common.CommentData;
+import com.googlesource.gerrit.plugins.chatgpt.model.gerrit.GerritComment;
+import com.googlesource.gerrit.plugins.chatgpt.model.common.GerritClientData;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@Slf4j
+public abstract class ChatGptUserPromptBase {
+ protected final GerritClientData gerritClientData;
+ protected final HashMap<String, FileDiffProcessed> fileDiffsProcessed;
+ protected final CommentData commentData;
+ @Getter
+ protected final List<ChatGptHistoryItem> historyItems;
+
+ protected ChatGptHistory gptMessageHistory;
+ @Getter
+ protected List<GerritComment> commentProperties;
+
+ public ChatGptUserPromptBase(Configuration config, GerritChange change, GerritClientData gerritClientData) {
+ this.gerritClientData = gerritClientData;
+ fileDiffsProcessed = gerritClientData.getFileDiffsProcessed();
+ commentData = gerritClientData.getCommentData();
+ gptMessageHistory = new ChatGptHistory(config, change, gerritClientData);
+ historyItems = new ArrayList<>();
+ }
+
+ abstract void addHistoryItem(int i);
+
+ protected ChatGptHistoryItem getHistoryItem(int i) {
+ ChatGptHistoryItem historyItem = new ChatGptHistoryItem();
+ GerritComment commentProperty = commentProperties.get(i);
+ if (commentProperty.getLine() != null || commentProperty.getRange() != null) {
+ String filename = commentProperty.getFilename();
+ InlineCode inlineCode = new InlineCode(fileDiffsProcessed.get(filename));
+ historyItem.setFilename(filename);
+ historyItem.setLineNumber(commentProperty.getLine());
+ historyItem.setCodeSnippet(inlineCode.getInlineCode(commentProperty));
+ }
+
+ return historyItem;
+ }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptRequests.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptRequests.java
new file mode 100644
index 0000000..4eff48c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptRequests.java
@@ -0,0 +1,29 @@
+package com.googlesource.gerrit.plugins.chatgpt.client.prompt;
+
+import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptHistoryItem;
+import com.googlesource.gerrit.plugins.chatgpt.model.common.GerritClientData;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ChatGptUserPromptRequests extends ChatGptUserPromptBase {
+ public ChatGptUserPromptRequests(Configuration config, GerritChange change, GerritClientData gerritClientData) {
+ super(config, change, gerritClientData);
+ commentProperties = commentData.getCommentProperties();
+ }
+
+ public void addHistoryItem(int i) {
+ ChatGptHistoryItem requestItem = getHistoryItem(i);
+ requestItem.setId(i);
+ historyItems.add(requestItem);
+ }
+
+ protected ChatGptHistoryItem getHistoryItem(int i) {
+ ChatGptHistoryItem requestItem = super.getHistoryItem(i);
+ requestItem.setRequest(gptMessageHistory.retrieveHistory(commentProperties.get(i)));
+
+ return requestItem;
+ }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptReview.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptReview.java
new file mode 100644
index 0000000..6cb7c79
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/prompt/ChatGptUserPromptReview.java
@@ -0,0 +1,47 @@
+package com.googlesource.gerrit.plugins.chatgpt.client.prompt;
+
+import com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import com.googlesource.gerrit.plugins.chatgpt.model.chatgpt.ChatGptHistoryItem;
+import com.googlesource.gerrit.plugins.chatgpt.model.common.GerritClientData;
+import com.googlesource.gerrit.plugins.chatgpt.model.gerrit.GerritComment;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+
+import static com.googlesource.gerrit.plugins.chatgpt.client.gerrit.GerritClientComments.GLOBAL_MESSAGES_FILENAME;
+
+@Slf4j
+public class ChatGptUserPromptReview extends ChatGptUserPromptBase {
+ private boolean patchSetDone;
+
+ public ChatGptUserPromptReview(Configuration config, GerritChange change, GerritClientData gerritClientData) {
+ super(config, change, gerritClientData);
+ commentProperties = new ArrayList<>(commentData.getCommentMap().values());
+ patchSetDone = false;
+ }
+
+ public void addHistoryItem(int i) {
+ ChatGptHistoryItem messageItem = getHistoryItem(i);
+ if (messageItem.getMessage() != null) {
+ historyItems.add(messageItem);
+ }
+ }
+
+ protected ChatGptHistoryItem getHistoryItem(int i) {
+ ChatGptHistoryItem messageItem = super.getHistoryItem(i);
+ GerritComment commentProperty = commentProperties.get(i);
+ if (commentProperty.getFilename().equals(GLOBAL_MESSAGES_FILENAME)) {
+ if (!patchSetDone) {
+ messageItem.setMessage(gptMessageHistory.retrieveHistory(commentProperty));
+ patchSetDone = true;
+ }
+ }
+ else {
+ messageItem.setMessage(gptMessageHistory.retrieveHistory(commentProperty));
+ }
+
+ return messageItem;
+ }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventListenerHandler.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventListenerHandler.java
index a58e3bc..4a259d4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventListenerHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventListenerHandler.java
@@ -187,6 +187,9 @@
}
log.debug("Flag `isCommentEvent` set to {}", isCommentEvent);
change.setIsCommentEvent(isCommentEvent);
+ if (!isCommentEvent) {
+ gerritClient.retrieveAllComments(change);
+ }
return true;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptRequestItem.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptHistoryItem.java
similarity index 67%
rename from src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptRequestItem.java
rename to src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptHistoryItem.java
index 9b1fc7e..48df7e1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptRequestItem.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptHistoryItem.java
@@ -5,6 +5,7 @@
@EqualsAndHashCode(callSuper = true)
@Data
-public class ChatGptRequestItem extends ChatGptDialogueItem {
+public class ChatGptHistoryItem extends ChatGptDialogueItem {
private String request;
+ private String message;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptReplyItem.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptReplyItem.java
index c4a142e..633cc67 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptReplyItem.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/model/chatgpt/ChatGptReplyItem.java
@@ -7,4 +7,6 @@
@Data
public class ChatGptReplyItem extends ChatGptDialogueItem {
private String reply;
+ private boolean repeated;
+ private boolean conflicting;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/settings/DynamicSettings.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/settings/DynamicSettings.java
index aedc40b..3cf9bea 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/settings/DynamicSettings.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/settings/DynamicSettings.java
@@ -31,10 +31,10 @@
public static void update(Configuration config, GerritChange change, GerritClient gerritClient) {
Settings settings = getInstance(change);
GerritClientData gerritClientData = gerritClient.getClientData(change);
- ChatGptUserPrompt chatGptUserPrompt = new ChatGptUserPrompt(config, gerritClientData);
+ ChatGptUserPrompt chatGptUserPrompt = new ChatGptUserPrompt(config, change, gerritClientData);
settings.setCommentPropertiesSize(gerritClientData.getCommentProperties().size());
- settings.setGptRequestUserPrompt(chatGptUserPrompt.buildPrompt(change));
+ settings.setGptRequestUserPrompt(chatGptUserPrompt.buildPrompt());
if (config.isVotingEnabled() && !change.getIsCommentEvent()) {
GerritPermittedVotingRange permittedVotingRange = gerritClient.getPermittedVotingRange(change);
if (permittedVotingRange != null) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/StringUtils.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/StringUtils.java
index 21ed900..c2596e9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/StringUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/StringUtils.java
@@ -4,6 +4,8 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
public class StringUtils {
@@ -21,11 +23,25 @@
resultChunks.addAll(Arrays.asList(leftDelimReplacement, chunk, rightDelimReplacement));
}
}
- return String.join("", resultChunks);
+ return concatenate(resultChunks);
}
public static String parseOutOfDelimiters(String body, String splitDelim, Function<String, String> processMessage) {
return parseOutOfDelimiters(body, splitDelim, processMessage, splitDelim, splitDelim);
}
+ public static List<String> getNumberedList(List<String> components) {
+ return IntStream.range(0, components.size())
+ .mapToObj(i -> (i + 1) + ". " + components.get(i))
+ .collect(Collectors.toList());
+ }
+
+ public static String concatenate(List<String> components) {
+ return String.join("", components);
+ }
+
+ public static String joinWithNewLine(List<String> components) {
+ return String.join("\n", components);
+ }
+
}
diff --git a/src/main/resources/Config/prompts.json b/src/main/resources/Config/prompts.json
index 0e9a229..36e1f9b 100644
--- a/src/main/resources/Config/prompts.json
+++ b/src/main/resources/Config/prompts.json
@@ -1,13 +1,23 @@
{
"DEFAULT_GPT_SYSTEM_PROMPT": "Act as a PatchSet Reviewer",
- "DEFAULT_GPT_SYSTEM_PROMPT_INSTRUCTIONS": "I will provide you with PatchSet Diffs for various files in a JSON format. Each changed file's content will be detailed in the \"content\" field of the JSON object. In this \"content\", the \"a\" items are the lines removed, the \"b\" items are the lines added, and the \"ab\" items are the unchanged lines. In your response, avoid explicitly referring to the \"a\", \"b\", and other fields from the JSON object. Instead, use more intuitive terms like \"new lines\" for additions, \"removed lines\" for deletions, and \"unchanged lines\" for the parts that haven't been altered.",
- "DEFAULT_GPT_REVIEW_USER_PROMPT": "Focus your review on the \"a\" and \"b\" items, but use the \"ab\" items as context to understand the changes better. Provide insights on any potential issues you foresee and suggestions for improvements if necessary. Concentrate exclusively on spotting and rectifying issues; avoid mentioning any positive elements. For instance, instead of saying \"this is good, but that needs improvement\", simply state \"that needs improvement\".",
- "DEFAULT_GPT_JSON_USER_PROMPT": "Each reply must be formatted as an individual object within an array in the key `replies`. The object will always contain the string field `reply`",
- "DEFAULT_GPT_REQUEST_JSON_USER_PROMPT": "along with the key `id`, which corresponds to the `id` value from the related request in the request JSON array",
- "DEFAULT_GPT_JSON_USER_PROMPT_2": "Also return back the field `changeId` provided in the corresponding request. For replies that are specific to a certain part of the code, the object must additionally include the keys `filename`, `lineNumber`, and `codeSnippet` to precisely identify the relevant code section.",
- "DEFAULT_GPT_JSON_USER_PROMPT_ENFORCE_RESPONSE_CHECK": "Make sure that the array in `replies` contains exactly %d element(s), one for each request.",
- "DEFAULT_GPT_REQUEST_USER_PROMPT_1": "I have some requests about the following PatchSet Diff:",
- "DEFAULT_GPT_REQUEST_USER_PROMPT_2": "My requests are given in a JSON-formatted array:",
- "DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT": "Also, perform a check on the commit message of the PatchSet. The commit message is provided in the \"content\" field of \"/COMMIT_MSG\" in the same way as the file changes. Ensure that the commit message accurately and succinctly describes the changes made, and verify if it matches the nature and scope of the changes in the PatchSet. If your feedback on the commit message is negative, you are required to supply an example of commit message that meets these criteria. For instance, if your comment is \"The commit message lacks detail\", you should follow up with \"A clearer commit message would be '...'\".",
- "DEFAULT_GPT_VOTING_REVIEW_USER_PROMPT": "Additionally, assign a score to the current PatchSet. The score must be an integer from %d to %d, calculated as the lower of the code or commit message error assessments. For example, if code issues rate +1 but the commit message is -1, the final score is -1."
+ "DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION": "I will provide you with PatchSet Diffs for various files in a JSON format. Each changed file's content will be detailed in the \"content\" field of the JSON object. In this \"content\", the \"a\" items are the lines removed, the \"b\" items are the lines added, and the \"ab\" items are the unchanged lines. In your response, avoid explicitly referring to the \"a\", \"b\", and other fields from the JSON object. Instead, use more intuitive terms like \"new lines\" for additions, \"removed lines\" for deletions, and \"unchanged lines\" for the parts that haven't been altered.",
+ "DEFAULT_GPT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW": "Also, I will supply the history of messages exchanged related to the PatchSet.",
+ "DEFAULT_GPT_REVIEW_PROMPT": "To conduct your review, follow these steps in the given order:",
+ "DEFAULT_GPT_REVIEW_PROMPT_REVIEW": "Begin with examining the PatchSet Diff, focusing on the \"a\" and \"b\" items, and using the \"ab\" items as context to understand the changes better. Provide insights on any potential issues you foresee and suggestions for improvements if necessary. Concentrate exclusively on spotting and rectifying issues; avoid mentioning any positive elements. For instance, instead of saying \"this is good, but that needs improvement\", simply state \"that needs improvement\".",
+ "DEFAULT_GPT_REVIEW_PROMPT_DIFF": "Here are the PatchSet Diffs:",
+ "DEFAULT_GPT_REVIEW_PROMPT_MESSAGE_HISTORY": "Here is the message history:",
+ "DEFAULT_GPT_REVIEW_PROMPT_COMMIT_MESSAGES": "Review the commit message of the PatchSet and provide your feedback in an additional reply. The commit message is provided in the \"content\" field of \"/COMMIT_MSG\" in the same way as the file changes. Ensure that the commit message accurately and succinctly describes the changes made, and verify if it matches the nature and scope of the changes in the PatchSet. If your feedback on the commit message is negative, you are required to supply an example of commit message that meets these criteria. For instance, if your comment is \"The commit message lacks detail\", you should follow up with \"A clearer commit message would be '...'\".",
+ "DEFAULT_GPT_REVIEW_PROMPT_VOTING": "Assign a score to the current PatchSet based on your replies. The score must be an integer from %d to %d, calculated as the lower of the code or commit message error assessments. For example, if code issues rate +1 but the commit message is -1, the final score is -1.",
+ "DEFAULT_GPT_REQUEST_PROMPT_DIFF": "I have some requests about the following PatchSet Diff:",
+ "DEFAULT_GPT_REQUEST_PROMPT_REQUESTS": "My requests are given in a JSON-formatted array:",
+ "DEFAULT_GPT_REPLIES_PROMPT": "Each reply must be formatted as an individual object within an array in the key `replies`. The object includes the string attributes %s, with the following specifications: %s.",
+ "DEFAULT_GPT_REPLIES_PROMPT_INLINE": "For replies that are specific to a certain part of the code, the object must additionally include the keys `filename`, `lineNumber`, and `codeSnippet` to precisely identify the relevant code section.",
+ "DEFAULT_GPT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK": "Make sure that the array in `replies` contains exactly %d element(s), one for each request.",
+ "DEFAULT_GPT_REPLIES_ATTRIBUTES": {
+ "reply": "contains the text of the response",
+ "id": "corresponds to the `id` value from the related request in the request JSON array",
+ "changeId": "must be included in the response, mirroring the value provided in the corresponding request",
+ "repeated": "is marked true if any message in the history either contains the same core message as the `reply` or addresses the same code snippet, and is marked false otherwise",
+ "conflicting": "is marked true if any message in the history, identified by the 'assistant' role, is in conflict with the reply, and false if there is no conflict"
+ }
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
index da63f59..11e50ba 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
@@ -50,6 +50,7 @@
import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
import static com.googlesource.gerrit.plugins.chatgpt.client.UriResourceLocator.*;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.StringUtils.joinWithNewLine;
import static java.net.HttpURLConnection.HTTP_OK;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -80,10 +81,10 @@
private final Gson gson = new Gson();
private String expectedResponseStreamed;
- private String expectedSystemPrompt;
+ private String expectedSystemPromptReview;
private String reviewUserPrompt;
private String reviewVoteUserPrompt;
- private String reviewTagComments;
+ private String promptTagComments;
private String diffContent;
private String gerritPatchSetReview;
@@ -93,6 +94,7 @@
private PluginConfig globalConfig;
private PluginConfig projectConfig;
private Configuration config;
+ private ChatGptPrompt chatGptPrompt;
@Before
public void before() throws IOException {
@@ -137,7 +139,12 @@
when(projectConfig.getBoolean(Mockito.eq("isEnabled"), Mockito.anyBoolean())).thenReturn(true);
config = new Configuration(globalConfig, projectConfig);
- new ChatGptPrompt(config);
+
+ // Mock the config instance values
+ when(config.getGerritUserName()).thenReturn(GERRIT_GPT_USERNAME);
+
+ // Load the prompts
+ chatGptPrompt = new ChatGptPrompt(config);
}
private void setupMockRequests() {
@@ -226,20 +233,28 @@
gerritPatchSetReview = new String(Files.readAllBytes(basePath.resolve("__files/gerritPatchSetReview.json")));
expectedResponseStreamed = new String(Files.readAllBytes(basePath.resolve(
"__files/chatGptExpectedResponseStreamed.json")));
- reviewTagComments = new String(Files.readAllBytes(basePath.resolve("__files/chatGptReviewTagComments.json")));
- expectedSystemPrompt = ChatGptPrompt.getDefaultSystemPrompt();
- reviewUserPrompt = String.join("\n", Arrays.asList(
- ChatGptPrompt.DEFAULT_GPT_REVIEW_USER_PROMPT,
- ChatGptPrompt.getPatchSetReviewUserPrompt(),
- ChatGptPrompt.DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT,
- diffContent
+ String promptTagReview = new String(Files.readAllBytes(basePath.resolve(
+ "__files/chatGptPromptTagReview.json")));
+ promptTagComments = new String(Files.readAllBytes(basePath.resolve("__files/chatGptPromptTagRequests.json")));
+ expectedSystemPromptReview = ChatGptPrompt.getDefaultGptReviewSystemPrompt();
+ reviewUserPrompt = joinWithNewLine(Arrays.asList(
+ ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT,
+ "1. " + ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_REVIEW + " " + ChatGptPrompt.getPatchSetReviewUserPrompt(),
+ "2. " + ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_COMMIT_MESSAGES,
+ ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_DIFF,
+ diffContent,
+ ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_MESSAGE_HISTORY,
+ promptTagReview
));
- reviewVoteUserPrompt = String.join("\n", Arrays.asList(
- ChatGptPrompt.DEFAULT_GPT_REVIEW_USER_PROMPT,
- ChatGptPrompt.getPatchSetReviewUserPrompt(),
- ChatGptPrompt.DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT,
- String.format(ChatGptPrompt.DEFAULT_GPT_VOTING_REVIEW_USER_PROMPT, VOTING_MIN_SCORE, VOTING_MAX_SCORE),
- diffContent
+ reviewVoteUserPrompt = joinWithNewLine(Arrays.asList(
+ ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT,
+ "1. " + ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_REVIEW + " " + ChatGptPrompt.getPatchSetReviewUserPrompt(),
+ "2. " + ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_COMMIT_MESSAGES,
+ "3. " + String.format(ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_VOTING, VOTING_MIN_SCORE, VOTING_MAX_SCORE),
+ ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_DIFF,
+ diffContent,
+ ChatGptPrompt.DEFAULT_GPT_REVIEW_PROMPT_MESSAGE_HISTORY,
+ promptTagReview
));
}
@@ -265,6 +280,7 @@
PatchSetReviewer patchSetReviewer = new PatchSetReviewer(gerritClient, chatGptClient);
ConfigCreator mockConfigCreator = mock(ConfigCreator.class);
when(mockConfigCreator.createConfig(ArgumentMatchers.any())).thenReturn(config);
+ chatGptPrompt.setCommentEvent(false);
PatchSetCreatedEvent event = mock(PatchSetCreatedEvent.class);
when(event.getProjectNameKey()).thenReturn(PROJECT_NAME);
@@ -286,7 +302,7 @@
JsonObject gptRequestBody = gson.fromJson(chatGptClient.getRequestBody(), JsonObject.class);
JsonArray prompts = gptRequestBody.get("messages").getAsJsonArray();
String systemPrompt = prompts.get(0).getAsJsonObject().get("content").getAsString();
- Assert.assertEquals(expectedSystemPrompt, systemPrompt);
+ Assert.assertEquals(expectedSystemPromptReview, systemPrompt);
String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
Assert.assertEquals(reviewUserPrompt, userPrompt);
String requestBody = loggedRequests.get(0).getBodyAsString();
@@ -305,6 +321,7 @@
PatchSetReviewer patchSetReviewer = new PatchSetReviewer(gerritClient, chatGptClient);
ConfigCreator mockConfigCreator = mock(ConfigCreator.class);
when(mockConfigCreator.createConfig(ArgumentMatchers.any())).thenReturn(config);
+ chatGptPrompt.setCommentEvent(false);
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getGptDomain()
+ UriResourceLocator.chatCompletionsUri()).getPath()))
.willReturn(WireMock.aResponse()
@@ -372,12 +389,13 @@
ConfigCreator mockConfigCreator = mock(ConfigCreator.class);
when(config.getGerritUserName()).thenReturn(GERRIT_GPT_USERNAME);
when(mockConfigCreator.createConfig(ArgumentMatchers.any())).thenReturn(config);
+ chatGptPrompt.setCommentEvent(true);
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getGptDomain()
+ UriResourceLocator.chatCompletionsUri()).getPath()))
.willReturn(WireMock.aResponse()
.withStatus(HTTP_OK)
.withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
- .withBodyFile("chatGptResponseComments.json")));
+ .withBodyFile("chatGptResponseRequests.json")));
CommentAddedEvent event = mock(CommentAddedEvent.class);
when(event.getProjectNameKey()).thenReturn(PROJECT_NAME);
@@ -395,11 +413,11 @@
CompletableFuture<Void> future = eventListenerHandler.getLatestFuture();
future.get();
- String commentUserPrompt = String.join("\n", Arrays.asList(
- ChatGptPrompt.DEFAULT_GPT_REQUEST_USER_PROMPT_1,
+ String commentUserPrompt = joinWithNewLine(Arrays.asList(
+ ChatGptPrompt.DEFAULT_GPT_REQUEST_PROMPT_DIFF,
diffContent,
- ChatGptPrompt.DEFAULT_GPT_REQUEST_USER_PROMPT_2,
- reviewTagComments,
+ ChatGptPrompt.DEFAULT_GPT_REQUEST_PROMPT_REQUESTS,
+ promptTagComments,
ChatGptPrompt.getCommentRequestUserPrompt(commentPropertiesSize)
));
RequestPatternBuilder requestPatternBuilder = WireMock.postRequestedFor(
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/integration/CodeReviewPluginIT.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/integration/CodeReviewPluginIT.java
index 1502fdf..27643ff 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/integration/CodeReviewPluginIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/integration/CodeReviewPluginIT.java
@@ -36,7 +36,7 @@
@Test
public void sayHelloToGPT() throws Exception {
- ChatGptPrompt chatGptPrompt = new ChatGptPrompt(config);
+ ChatGptPrompt chatGptPrompt = new ChatGptPrompt(config, true);
when(config.getGptDomain()).thenReturn(Configuration.OPENAI_DOMAIN);
when(config.getGptToken()).thenReturn("Your GPT token");
when(config.getGptModel()).thenReturn(Configuration.DEFAULT_GPT_MODEL);
diff --git a/src/test/resources/__files/chatGptPromptTagRequests.json b/src/test/resources/__files/chatGptPromptTagRequests.json
new file mode 100644
index 0000000..a0a6b81
--- /dev/null
+++ b/src/test/resources/__files/chatGptPromptTagRequests.json
@@ -0,0 +1 @@
+[{"request":"[{\"role\":\"assistant\",\"content\":\"The commit message \\u0027Minor Fixes\\u0027 is too vague and does not provide enough context about the changes made. A more descriptive message that outlines the specific fixes or improvements would be beneficial.\"},{\"role\":\"user\",\"content\":\"can you suggest an alternative commit message?\"},{\"role\":\"assistant\",\"content\":\"How about something like \\\"Corrected Indentation in Module-Class Retrieval Line\\\"?\"}]","id":0},{"request":"[{\"role\":\"user\",\"content\":\"message\"}]","id":1,"filename":"test_file.py","lineNumber":5,"codeSnippet":"TypeClassOrPath"},{"request":"[{\"role\":\"assistant\",\"content\":\"message from gpt\"},{\"role\":\"user\",\"content\":\"message 2\"}]","id":2,"filename":"test_file.py","lineNumber":5,"codeSnippet":"TypeClassOrPath"}]
\ No newline at end of file
diff --git a/src/test/resources/__files/chatGptPromptTagReview.json b/src/test/resources/__files/chatGptPromptTagReview.json
new file mode 100644
index 0000000..dc835d5
--- /dev/null
+++ b/src/test/resources/__files/chatGptPromptTagReview.json
@@ -0,0 +1 @@
+[{"message":"[{\"role\":\"assistant\",\"content\":\"message from gpt\"},{\"role\":\"user\",\"content\":\"message 2\"}]","filename":"test_file.py","lineNumber":5,"codeSnippet":"TypeClassOrPath"},{"message":"[{\"role\":\"assistant\",\"content\":\"The commit message \\u0027Minor Fixes\\u0027 is too vague and does not provide enough context about the changes made. A more descriptive message that outlines the specific fixes or improvements would be beneficial.\"},{\"role\":\"user\",\"content\":\"can you suggest an alternative commit message?\"},{\"role\":\"assistant\",\"content\":\"How about something like \\\"Corrected Indentation in Module-Class Retrieval Line\\\"?\"}]"},{"message":"[{\"role\":\"assistant\",\"content\":\"message from gpt\"}]","filename":"test_file.py","lineNumber":5,"codeSnippet":"TypeClassOrPath"}]
\ No newline at end of file
diff --git a/src/test/resources/__files/chatGptResponseComments.json b/src/test/resources/__files/chatGptResponseRequests.json
similarity index 100%
rename from src/test/resources/__files/chatGptResponseComments.json
rename to src/test/resources/__files/chatGptResponseRequests.json
diff --git a/src/test/resources/__files/chatGptReviewTagComments.json b/src/test/resources/__files/chatGptReviewTagComments.json
deleted file mode 100644
index 07d74bc..0000000
--- a/src/test/resources/__files/chatGptReviewTagComments.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"request":"[{\"role\":\"assistant\",\"content\":\"The commit message \\u0027Minor Fixes\\u0027 is too vague and does not provide enough context about the changes made. A more descriptive message that outlines the specific fixes or improvements would be beneficial.\"},{\"role\":\"user\",\"content\":\"can you suggest an alternative commit message?\"},{\"role\":\"assistant\",\"content\":\"How about something like \\\"Corrected Indentation in Module-Class Retrieval Line\\\"?\"}]","id":0},{"request":"message","id":1,"filename":"test_file.py","lineNumber":5,"codeSnippet":"TypeClassOrPath"},{"request":"[{\"role\":\"assistant\",\"content\":\"message from gpt\"},{\"role\":\"user\",\"content\":\"message 2\"}]","id":2,"filename":"test_file.py","lineNumber":5,"codeSnippet":"TypeClassOrPath"}]
\ No newline at end of file