Support Point-by-Point approach in comments

Enhanced comments to support a synthetic point-by-point approach,
mirroring the existing method used for Patchset reviews.
With this change, the stream output mode is always set to false for
comment inquiries, regardless of the `gptStreamOutput` config setting
value.

Jira-Id: IT-103
Change-Id: I319fa42887201ca74b9141b207fc094f112454a9
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 6b2918e..cb45a74 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/PatchSetReviewer.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/PatchSetReviewer.java
@@ -8,7 +8,7 @@
 import com.googlesource.gerrit.plugins.chatgpt.client.GerritClient;
 import com.googlesource.gerrit.plugins.chatgpt.client.InlineCode;
 import com.googlesource.gerrit.plugins.chatgpt.client.OpenAiClient;
-import com.googlesource.gerrit.plugins.chatgpt.client.model.ChatGptSuggestion;
+import com.googlesource.gerrit.plugins.chatgpt.client.model.ChatGptSuggestionPoint;
 import com.googlesource.gerrit.plugins.chatgpt.client.model.GerritCommentRange;
 import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
 import lombok.extern.slf4j.Slf4j;
@@ -29,10 +29,9 @@
     private final OpenAiClient openAiClient;
 
     private List<HashMap<String, Object>> reviewBatches;
-    private String currentTag;
     private List<JsonObject> commentProperties;
     private HashMap<String, List<String>> filesNewContent;
-    private boolean isPatchSetEvent;
+    private boolean isCommentEvent;
 
     @Inject
     PatchSetReviewer(GerritClient gerritClient, OpenAiClient openAiClient) {
@@ -52,7 +51,7 @@
 
         String reviewSuggestion = getReviewSuggestion(config, fullChangeId, patchSet);
         log.info("ChatGPT response: {}", reviewSuggestion);
-        if (isPatchSetEvent && config.getGptReviewByPoints()) {
+        if (isCommentEvent || config.getGptReviewByPoints()) {
             retrieveReviewFromJson(reviewSuggestion);
         }
         else {
@@ -62,11 +61,11 @@
         gerritClient.postComments(fullChangeId, reviewBatches);
     }
 
-    public void setIsPatchSetEvent(boolean isPatchSetEvent) {
-        this.isPatchSetEvent = isPatchSetEvent;
+    public void setIsCommentEvent(boolean isCommentEvent) {
+        this.isCommentEvent = isCommentEvent;
     }
 
-    private Integer getBatchId() {
+    private Integer getBatchId(String currentTag) {
         try {
             return Integer.parseInt(currentTag);
         }
@@ -75,14 +74,9 @@
         }
     }
 
-    private void addReviewBatch(StringBuilder batch) {
+    private void addReviewBatch(Integer batchID, String batch) {
         HashMap<String, Object> batchMap = new HashMap<>();
-        batchMap.put("content", batch.toString());
-        Integer batchID = getBatchId();
-        if (batchID == null) {
-            log.warn("Error retrieving batch ID from currentTag {}", currentTag);
-            return;
-        }
+        batchMap.put("content", batch);
         if (commentProperties != null && batchID < commentProperties.size()) {
             JsonObject commentProperty = commentProperties.get(batchID);
             if (commentProperty != null &&
@@ -102,7 +96,7 @@
         reviewBatches.add(batchMap);
     }
 
-    private Optional<GerritCommentRange> getGerritCommentRange(ChatGptSuggestion suggestion) {
+    private Optional<GerritCommentRange> getGerritCommentRange(ChatGptSuggestionPoint suggestion) {
         Optional<GerritCommentRange> gerritCommentRange = Optional.empty();
         if (suggestion.getFilename() == null) {
             return gerritCommentRange;
@@ -125,40 +119,45 @@
 
     private void retrieveReviewFromJson(String review) {
         review = review.replaceAll("^`*(?:json)?\\s*|\\s*`+$", "");
-        Type chatGptResponseListType = new TypeToken<List<ChatGptSuggestion>>(){}.getType();
-        List<ChatGptSuggestion> reviewJson = gson.fromJson(review, chatGptResponseListType);
+        Type chatGptResponseListType = new TypeToken<List<ChatGptSuggestionPoint>>(){}.getType();
+        List<ChatGptSuggestionPoint> reviewJson = gson.fromJson(review, chatGptResponseListType);
         filesNewContent = gerritClient.getFilesNewContent();
-        for (ChatGptSuggestion suggestion : reviewJson) {
+        for (ChatGptSuggestionPoint suggestion : reviewJson) {
             HashMap<String, Object> batchMap = new HashMap<>();
-            batchMap.put("content", suggestion.getSuggestion());
-            Optional<GerritCommentRange> optGerritCommentRange = getGerritCommentRange(suggestion);
-            if (optGerritCommentRange.isPresent()) {
-                GerritCommentRange gerritCommentRange = optGerritCommentRange.get();
-                batchMap.put("filename", suggestion.getFilename());
-                batchMap.put("line", gerritCommentRange.getStart_line());
-                batchMap.put("range", gerritCommentRange);
+            if (suggestion.getId() != null) {
+                addReviewBatch(suggestion.getId(), suggestion.getSuggestion());
             }
-            reviewBatches.add(batchMap);
+            else {
+                batchMap.put("content", suggestion.getSuggestion());
+                Optional<GerritCommentRange> optGerritCommentRange = getGerritCommentRange(suggestion);
+                if (optGerritCommentRange.isPresent()) {
+                    GerritCommentRange gerritCommentRange = optGerritCommentRange.get();
+                    batchMap.put("filename", suggestion.getFilename());
+                    batchMap.put("line", gerritCommentRange.getStart_line());
+                    batchMap.put("range", gerritCommentRange);
+                }
+                reviewBatches.add(batchMap);
+            }
         }
     }
 
     private void splitReviewIntoBatches(String review) {
         String[] lines = review.split("\n");
-        currentTag = "0";
+        Integer currentTag = 0;
         StringBuilder batch = new StringBuilder();
         for (int i = 0; i < lines.length; i++) {
             String[] extractResult = extractID(lines[i]);
             if (extractResult != null) {
                 log.debug("Captured '{}' from line '{}'", extractResult[0], lines[i]);
-                addReviewBatch(batch);
+                addReviewBatch(currentTag, batch.toString());
                 batch = new StringBuilder();
-                currentTag = extractResult[0];
+                currentTag = getBatchId(extractResult[0]);
                 lines[i] = extractResult[1];
             }
             batch.append(lines[i]).append("\n");
         }
         if (batch.length() > 0) {
-            addReviewBatch(batch);
+            addReviewBatch(currentTag, batch.toString());
         }
         log.info("Review batches created: {}", reviewBatches.size());
         log.debug("batches: {}", reviewBatches);
@@ -170,7 +169,7 @@
             log.warn("Patch set too large. Skipping review. changeId: {}", changeId);
             return String.format(SPLIT_REVIEW_MSG, config.getMaxReviewLines());
         }
-        return openAiClient.ask(config, patchSet);
+        return openAiClient.ask(config, patchSet, isCommentEvent);
     }
 }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java
index 8ab3f5d..cf2a574 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java
@@ -7,6 +7,7 @@
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
+import com.googlesource.gerrit.plugins.chatgpt.client.model.ChatGptRequestPoint;
 import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.entity.ContentType;
@@ -148,22 +149,21 @@
         return map;
     }
 
-    protected String getCommentPrompt(int i) {
-        StringBuilder commentString = new StringBuilder();
+    protected ChatGptRequestPoint getRequestPoint(int i) {
+        ChatGptRequestPoint requestPoint = new ChatGptRequestPoint();
         JsonObject commentProperty = commentProperties.get(i);
+        requestPoint.setId(i);
         if (commentProperty.has("line") || commentProperty.has("range")) {
             String filename = commentProperty.get("filename").getAsString();
             InlineCode inlineCode = new InlineCode(filesNewContent.get(filename));
-            commentString.append(String.format(Configuration.DEFAULT_GPT_CUSTOM_USER_CONTEXT_PROMPT,
-                    inlineCode.getInlineCode(commentProperty),
-                    commentProperty.get("line").getAsInt(),
-                    filename
-            ));
+            requestPoint.setFilename(filename);
+            requestPoint.setLineNumber(commentProperty.get("line").getAsInt());
+            requestPoint.setCodeSnippet(inlineCode.getInlineCode(commentProperty));
         }
         String commentMessage = commentProperty.get("message").getAsString();
-        commentString.append(removeMentionsFromComment(commentMessage).trim());
+        requestPoint.setRequest(removeMentionsFromComment(commentMessage).trim());
 
-        return commentString.toString();
+        return requestPoint;
     }
 
     public List<JsonObject> getCommentProperties() {
@@ -216,11 +216,11 @@
 
     public String getUserPrompt(HashMap<String, List<String>> filesNewContent) {
         this.filesNewContent = filesNewContent;
-        StringBuilder taggedPrompt = new StringBuilder();
+        List<ChatGptRequestPoint> requestPoints = new ArrayList<>();
         for (int i = 0; i < commentProperties.size(); i++) {
-            taggedPrompt.append(String.format("[ID:%d] %s\n", i, getCommentPrompt(i)));
+            requestPoints.add(getRequestPoint(i));
         }
-        return taggedPrompt.toString();
+        return requestPoints.isEmpty() ? "" : gson.toJson(requestPoints);
     }
 
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/InlineCode.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/InlineCode.java
index adba4e8..3d9123d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/InlineCode.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/InlineCode.java
@@ -2,7 +2,7 @@
 
 import com.google.gson.JsonObject;
 import com.google.gson.Gson;
-import com.googlesource.gerrit.plugins.chatgpt.client.model.ChatGptSuggestion;
+import com.googlesource.gerrit.plugins.chatgpt.client.model.ChatGptSuggestionPoint;
 import com.googlesource.gerrit.plugins.chatgpt.client.model.GerritCommentRange;
 import lombok.extern.slf4j.Slf4j;
 
@@ -49,7 +49,7 @@
         return Math.abs((range.end_line - range.start_line) / 2 - fromLine);
     }
 
-    public Optional<GerritCommentRange> findCommentRange(ChatGptSuggestion suggestion) {
+    public Optional<GerritCommentRange> findCommentRange(ChatGptSuggestionPoint suggestion) {
         int commentedLine;
         try {
             commentedLine = suggestion.getLineNumber();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java
index cd2b52b..0f5183e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java
@@ -28,6 +28,7 @@
             .disableHtmlEscaping()
             .create();
     private final HttpClientWithRetry httpClientWithRetry = new HttpClientWithRetry();
+    private boolean isCommentEvent = false;
 
     public String getRequestBody() {
         return requestBody;
@@ -44,14 +45,18 @@
         if (body == null) {
             throw new IOException("ChatGPT response body is null");
         }
-        String content = extractContent(config, body);
-        log.debug("ChatGPT response content: {}", content);
 
-        return content;
+        return extractContent(config, body);
+    }
+
+    public String ask(Configuration config, String patchSet, boolean isCommentEvent) throws Exception {
+        this.isCommentEvent = isCommentEvent;
+
+        return this.ask(config, patchSet);
     }
 
     public String extractContent(Configuration config, String body) throws Exception {
-        if (config.getGptStreamOutput()) {
+        if (config.getGptStreamOutput() && !isCommentEvent) {
             StringBuilder finalContent = new StringBuilder();
             try (BufferedReader reader = new BufferedReader(new StringReader(body))) {
                 String line;
@@ -98,7 +103,7 @@
                 .model(config.getGptModel())
                 .messages(messages)
                 .temperature(config.getGptTemperature())
-                .stream(config.getGptStreamOutput())
+                .stream(config.getGptStreamOutput() && !isCommentEvent)
                 .build();
 
         return gson.toJson(chatCompletionRequest);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptPointBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptPointBase.java
new file mode 100644
index 0000000..375f457
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptPointBase.java
@@ -0,0 +1,11 @@
+package com.googlesource.gerrit.plugins.chatgpt.client.model;
+
+import lombok.Data;
+
+@Data
+public class ChatGptPointBase {
+    protected Integer id;
+    protected String filename;
+    protected Integer lineNumber;
+    protected String codeSnippet;
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptRequestPoint.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptRequestPoint.java
new file mode 100644
index 0000000..c202125
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptRequestPoint.java
@@ -0,0 +1,8 @@
+package com.googlesource.gerrit.plugins.chatgpt.client.model;
+
+import lombok.Data;
+
+@Data
+public class ChatGptRequestPoint extends ChatGptPointBase {
+    private String request;
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptSuggestion.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptSuggestion.java
deleted file mode 100644
index 3acc2f6..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptSuggestion.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.googlesource.gerrit.plugins.chatgpt.client.model;
-
-import lombok.Data;
-
-@Data
-public class ChatGptSuggestion {
-    private String suggestion;
-    private String filename;
-    private Integer lineNumber;
-    private String codeSnippet;
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptSuggestionPoint.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptSuggestionPoint.java
new file mode 100644
index 0000000..39c7e57
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/model/ChatGptSuggestionPoint.java
@@ -0,0 +1,8 @@
+package com.googlesource.gerrit.plugins.chatgpt.client.model;
+
+import lombok.Data;
+
+@Data
+public class ChatGptSuggestionPoint extends ChatGptPointBase {
+    private String suggestion;
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java
index 60d721d..51f82a6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java
@@ -25,20 +25,22 @@
     public static final String DEFAULT_GPT_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 whether the changes " +
             "make sense, any potential issues you foresee, and suggestions for improvements if necessary.";
-    public static final String DEFAULT_GPT_USER_PROMPT_JSON = "Provide your feedback in a JSON format. Each " +
+    public static final String DEFAULT_GPT_USER_PROMPT_JSON = "Provide your response in a JSON format. Each " +
             "suggestion must be formatted as an individual object within an array. The object will always contain " +
-            "the key `suggestion`. For suggestions that are specific to a certain part of the code, the object " +
-            "should additionally include the keys `filename`, `lineNumber`, and `codeSnippet` to precisely identify " +
-            "the relevant code section.";
+            "the key `suggestion`";
+    public static final String DEFAULT_GPT_CUSTOM_USER_PROMPT_JSON = " along with the key `id`, which corresponds to " +
+            "the `id` value from the related request in the request JSON array";
+    public static final String DEFAULT_GPT_USER_PROMPT_JSON_2 = ". For suggestions that are specific to a certain " +
+            "part of the code, the object should additionally include the keys `filename`, `lineNumber`, and " +
+            "`codeSnippet` to precisely identify the relevant code section.";
     public static final String DEFAULT_GPT_CUSTOM_USER_PROMPT_1 = "I have some requests about the following PatchSet " +
             "Diff:";
-    public static final String DEFAULT_GPT_CUSTOM_USER_PROMPT_2 = "Here are my requests:";
-    public static final String DEFAULT_GPT_CUSTOM_USER_CONTEXT_PROMPT = "In reference to the code `%s` (from line %d " +
-            "of file \"%s\"), ";
+    public static final String DEFAULT_GPT_CUSTOM_USER_PROMPT_2 = "My requests are given in an array and formatted " +
+            "in JSON :";
     public static final String 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.";
+            "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.";
     public static final String NOT_CONFIGURED_ERROR_MSG = "%s is not configured";
     public static final String KEY_GPT_SYSTEM_PROMPT = "gptSystemPrompt";
     public static final String KEY_GPT_USER_PROMPT = "gptUserPrompt";
@@ -169,13 +171,14 @@
                     DEFAULT_GPT_CUSTOM_USER_PROMPT_1,
                     patchSet,
                     DEFAULT_GPT_CUSTOM_USER_PROMPT_2,
-                    gptUserPrompt
+                    gptUserPrompt,
+                    DEFAULT_GPT_USER_PROMPT_JSON + DEFAULT_GPT_CUSTOM_USER_PROMPT_JSON + DEFAULT_GPT_USER_PROMPT_JSON_2
             ));
         }
         else {
             prompt.add(getString(KEY_GPT_USER_PROMPT, DEFAULT_GPT_USER_PROMPT));
             if (getGptReviewByPoints()) {
-                prompt.add(DEFAULT_GPT_USER_PROMPT_JSON);
+                prompt.add(DEFAULT_GPT_USER_PROMPT_JSON + DEFAULT_GPT_USER_PROMPT_JSON_2);
             }
             if (getGptReviewCommitMessages()) {
                 prompt.add(DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT);
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 9e80650..df12b59 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
@@ -149,14 +149,14 @@
                 if (!isPatchSetReviewEnabled(patchSetEvent)) {
                     return;
                 }
-                reviewer.setIsPatchSetEvent(true);
+                reviewer.setIsCommentEvent(false);
                 break;
             case "comment-added":
                 if (!gerritClient.retrieveLastComments(event, fullChangeId)) {
                     log.info("No comments found for review");
                     return;
                 }
-                reviewer.setIsPatchSetEvent(false);
+                reviewer.setIsCommentEvent(true);
                 break;
             default:
                 return;
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 294a9a8..bf2553f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
@@ -72,8 +72,8 @@
     private static final BranchNameKey BRANCH_NAME = BranchNameKey.create(PROJECT_NAME, "myBranchName");
     private static final boolean GPT_STREAM_OUTPUT = true;
     private static final long TEST_TIMESTAMP = 1699270812;
-    private static final String REVIEW_TAG_COMMENTS = "[ID:0] comment 2\n" +
-            "[ID:1] In reference to the code `TypeClassOrPath` (from line 5 of file \"test_file.py\"), message\n";
+    private static final String REVIEW_TAG_COMMENTS = "[{\"request\":\"comment 2\",\"id\":0},{\"request\":" +
+            "\"message\",\"id\":1,\"filename\":\"test_file.py\",\"lineNumber\":5,\"codeSnippet\":\"TypeClassOrPath\"}]";
 
     private final Gson gson = new Gson();
 
@@ -209,7 +209,7 @@
         ));
         reviewUserPromptByPoints = String.join("\n", Arrays.asList(
                 Configuration.DEFAULT_GPT_USER_PROMPT,
-                Configuration.DEFAULT_GPT_USER_PROMPT_JSON,
+                Configuration.DEFAULT_GPT_USER_PROMPT_JSON + Configuration.DEFAULT_GPT_USER_PROMPT_JSON_2,
                 Configuration.DEFAULT_GPT_COMMIT_MESSAGES_REVIEW_USER_PROMPT,
                 diffContent
         ));
@@ -217,7 +217,9 @@
                 Configuration.DEFAULT_GPT_CUSTOM_USER_PROMPT_1,
                 diffContent,
                 Configuration.DEFAULT_GPT_CUSTOM_USER_PROMPT_2,
-                REVIEW_TAG_COMMENTS
+                REVIEW_TAG_COMMENTS,
+                Configuration.DEFAULT_GPT_USER_PROMPT_JSON + Configuration.DEFAULT_GPT_CUSTOM_USER_PROMPT_JSON +
+                        Configuration.DEFAULT_GPT_USER_PROMPT_JSON_2
         ));
     }
 
@@ -285,6 +287,12 @@
         PatchSetReviewer patchSetReviewer = new PatchSetReviewer(gerritClient, openAiClient);
         ConfigCreator mockConfigCreator = mock(ConfigCreator.class);
         when(mockConfigCreator.createConfig(ArgumentMatchers.any())).thenReturn(config);
+        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("chatGptResponseByPoints.json")));
 
         PatchSetCreatedEvent event = mock(PatchSetCreatedEvent.class);
         when(event.getProjectNameKey()).thenReturn(PROJECT_NAME);
@@ -294,13 +302,6 @@
         event.patchSet = this::createPatchSetAttribute;
         EventListenerHandler eventListenerHandler = new EventListenerHandler(patchSetReviewer, gerritClient);
 
-        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("chatGptResponseByPoints.json")));
-
         GerritListener gerritListener = new GerritListener(mockConfigCreator, eventListenerHandler);
         gerritListener.onEvent(event);
         CompletableFuture<Void> future = eventListenerHandler.getLatestFuture();
@@ -354,6 +355,12 @@
         ConfigCreator mockConfigCreator = mock(ConfigCreator.class);
         when(config.getGerritUserName()).thenReturn("gpt");
         when(mockConfigCreator.createConfig(ArgumentMatchers.any())).thenReturn(config);
+        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("chatGptResponseCommentsByPoints.json")));
 
         CommentAddedEvent event = mock(CommentAddedEvent.class);
         when(event.getProjectNameKey()).thenReturn(PROJECT_NAME);
diff --git a/src/test/resources/__files/chatGptResponseCommentsByPoints.json b/src/test/resources/__files/chatGptResponseCommentsByPoints.json
new file mode 100644
index 0000000..7b69870
--- /dev/null
+++ b/src/test/resources/__files/chatGptResponseCommentsByPoints.json
@@ -0,0 +1,11 @@
+{
+  "choices": [
+    {
+      "index": 0,
+      "message": {
+        "role": "assistant",
+        "content": "```json\n[\n    {\n      \"id\": 0,\n      \"suggestion\": \"The commit message 'Test Commit Message' is too vague and does not provide information about the specific changes made. A more detailed message is necessary to understand what has been fixed.\"\n    },\n    {\n      \"id\": 1,\n      \"suggestion\": \"Confirm that the method 'importclass' is meant to change its behavior when 'class_name' is None. The new lines suggest 'class_name' will be derived from the 'module_name' in such cases, which can have unintended effects if not explicitly intended.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 19,\n      \"codeSnippet\": \"if not class_name:\n        module_name, class_name = module_name.rsplit('.', 1)\"\n    }\n  ]\n```"
+      }
+    }
+  ]
+}