Merge changes I2c8adb06,I43613aa6,I3dde13c0

* changes:
  Use jgit @NonNull annotation
  Add license headers
  Format source code using google-java-format 1.24.0
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/Module.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/Module.java
index 9a4f4f9..39c937d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/Module.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
 import com.google.gerrit.server.events.EventListener;
@@ -6,9 +20,10 @@
 import com.googlesource.gerrit.plugins.aicodereview.listener.GerritListener;
 
 public class Module extends AbstractModule {
-    @Override
-    protected void configure() {
-        Multibinder<EventListener> eventListenerBinder = Multibinder.newSetBinder(binder(), EventListener.class);
-        eventListenerBinder.addBinding().to(GerritListener.class);
-    }
+  @Override
+  protected void configure() {
+    Multibinder<EventListener> eventListenerBinder =
+        Multibinder.newSetBinder(binder(), EventListener.class);
+    eventListenerBinder.addBinding().to(GerritListener.class);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/PatchSetReviewer.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/PatchSetReviewer.java
index 7bcea8f..4d4f41f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/PatchSetReviewer.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/PatchSetReviewer.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
 import com.google.inject.Inject;
@@ -11,166 +25,172 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientReview;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.messages.DebugCodeBlocksReview;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.comment.GerritCommentRange;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseContent;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritCodeRange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseContent;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.review.ReviewBatch;
 import com.googlesource.gerrit.plugins.aicodereview.settings.Settings;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
 import lombok.Getter;
 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 static final String SPLIT_REVIEW_MSG =
+      "Too many changes. Please consider splitting into patches smaller "
+          + "than %s lines for review.";
 
-    private final Configuration config;
-    private final GerritClient gerritClient;
-    private final ChangeSetData changeSetData;
-    private final Provider<GerritClientReview> clientReviewProvider;
-    @Getter
-    private final ChatAIClient chatGptClient;
-    private final Localizer localizer;
-    private final DebugCodeBlocksReview debugCodeBlocksReview;
+  private final Configuration config;
+  private final GerritClient gerritClient;
+  private final ChangeSetData changeSetData;
+  private final Provider<GerritClientReview> clientReviewProvider;
+  @Getter private final ChatAIClient chatGptClient;
+  private final Localizer localizer;
+  private final DebugCodeBlocksReview debugCodeBlocksReview;
 
-    private GerritCommentRange gerritCommentRange;
-    private List<ReviewBatch> reviewBatches;
-    private List<GerritComment> commentProperties;
-    private List<Integer> reviewScores;
+  private GerritCommentRange gerritCommentRange;
+  private List<ReviewBatch> reviewBatches;
+  private List<GerritComment> commentProperties;
+  private List<Integer> reviewScores;
 
-    @Inject
-    PatchSetReviewer(
-            GerritClient gerritClient,
-            Configuration config,
-            ChangeSetData changeSetData,
-            Provider<GerritClientReview> clientReviewProvider,
-            ChatAIClient chatGptClient,
-            Localizer localizer
-    ) {
-        this.config = config;
-        this.gerritClient = gerritClient;
-        this.changeSetData = changeSetData;
-        this.clientReviewProvider = clientReviewProvider;
-        this.chatGptClient = chatGptClient;
-        this.localizer = localizer;
-        debugCodeBlocksReview = new DebugCodeBlocksReview(localizer);
+  @Inject
+  PatchSetReviewer(
+      GerritClient gerritClient,
+      Configuration config,
+      ChangeSetData changeSetData,
+      Provider<GerritClientReview> clientReviewProvider,
+      ChatAIClient chatGptClient,
+      Localizer localizer) {
+    this.config = config;
+    this.gerritClient = gerritClient;
+    this.changeSetData = changeSetData;
+    this.clientReviewProvider = clientReviewProvider;
+    this.chatGptClient = chatGptClient;
+    this.localizer = localizer;
+    debugCodeBlocksReview = new DebugCodeBlocksReview(localizer);
+  }
+
+  public void review(GerritChange change) throws Exception {
+    reviewBatches = new ArrayList<>();
+    reviewScores = new ArrayList<>();
+    commentProperties = gerritClient.getClientData(change).getCommentProperties();
+    gerritCommentRange = new GerritCommentRange(gerritClient, change);
+    String patchSet = gerritClient.getPatchSet(change);
+    if (patchSet.isEmpty() && config.getAIMode() == Settings.Modes.stateless) {
+      log.info("No file to review has been found in the PatchSet");
+      return;
+    }
+    ChangeSetDataHandler.update(config, change, gerritClient, changeSetData, localizer);
+
+    if (changeSetData.shouldRequestAICodeReview()) {
+      AIChatResponseContent reviewReply = getReviewReply(change, patchSet);
+      log.debug("ChatGPT response: {}", reviewReply);
+
+      retrieveReviewBatches(reviewReply, change);
+    }
+    clientReviewProvider
+        .get()
+        .setReview(change, reviewBatches, changeSetData, getReviewScore(change));
+  }
+
+  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, AIChatReplyItem 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(AIChatResponseContent reviewReply, GerritChange change) {
+    if (reviewReply.getMessageContent() != null && !reviewReply.getMessageContent().isEmpty()) {
+      reviewBatches.add(new ReviewBatch(reviewReply.getMessageContent()));
+      return;
+    }
+    for (AIChatReplyItem replyItem : reviewReply.getReplies()) {
+      String reply = replyItem.getReply();
+      Integer score = replyItem.getScore();
+      boolean isNotNegative = isNotNegativeReply(score);
+      boolean isIrrelevant = isIrrelevantReply(replyItem);
+      boolean isHidden =
+          replyItem.isRepeated() || replyItem.isConflicting() || isIrrelevant || isNotNegative;
+      if (!replyItem.isConflicting() && !isIrrelevant && score != null) {
+        log.debug("Score added: {}", score);
+        reviewScores.add(score);
+      }
+      if (changeSetData.getReplyFilterEnabled() && isHidden) {
+        continue;
+      }
+      if (changeSetData.getDebugReviewMode()) {
+        reply += debugCodeBlocksReview.getDebugCodeBlock(replyItem, isHidden);
+      }
+      ReviewBatch batchMap = new ReviewBatch(reply);
+      if (change.getIsCommentEvent() && replyItem.getId() != null) {
+        setCommentBatchMap(batchMap, replyItem.getId());
+      } else {
+        setPatchSetReviewBatchMap(batchMap, replyItem);
+      }
+      reviewBatches.add(batchMap);
+    }
+  }
+
+  private AIChatResponseContent getReviewReply(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 new AIChatResponseContent(String.format(SPLIT_REVIEW_MSG, config.getMaxReviewLines()));
     }
 
-    public void review(GerritChange change) throws Exception {
-        reviewBatches = new ArrayList<>();
-        reviewScores = new ArrayList<>();
-        commentProperties = gerritClient.getClientData(change).getCommentProperties();
-        gerritCommentRange = new GerritCommentRange(gerritClient, change);
-        String patchSet = gerritClient.getPatchSet(change);
-        if (patchSet.isEmpty() && config.getAIMode() == Settings.Modes.stateless) {
-            log.info("No file to review has been found in the PatchSet");
-            return;
-        }
-        ChangeSetDataHandler.update(config, change, gerritClient, changeSetData, localizer);
+    return chatGptClient.ask(changeSetData, change, patchSet);
+  }
 
-        if (changeSetData.shouldRequestAICodeReview()) {
-            AIChatResponseContent reviewReply = getReviewReply(change, patchSet);
-            log.debug("ChatGPT response: {}", reviewReply);
-
-            retrieveReviewBatches(reviewReply, change);
-        }
-        clientReviewProvider.get().setReview(change, reviewBatches, changeSetData, getReviewScore(change));
+  private Integer getReviewScore(GerritChange change) {
+    if (config.isVotingEnabled()) {
+      return reviewScores.isEmpty()
+          ? (change.getIsCommentEvent() ? null : 0)
+          : Collections.min(reviewScores);
+    } else {
+      return null;
     }
+  }
 
-    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 boolean isNotNegativeReply(Integer score) {
+    return score != null
+        && config.getFilterNegativeComments()
+        && score >= config.getFilterCommentsBelowScore();
+  }
 
-    private void setPatchSetReviewBatchMap(ReviewBatch batchMap, AIChatReplyItem 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(AIChatResponseContent reviewReply, GerritChange change) {
-        if (reviewReply.getMessageContent() != null && !reviewReply.getMessageContent().isEmpty()) {
-            reviewBatches.add(new ReviewBatch(reviewReply.getMessageContent()));
-            return;
-        }
-        for (AIChatReplyItem replyItem : reviewReply.getReplies()) {
-            String reply = replyItem.getReply();
-            Integer score = replyItem.getScore();
-            boolean isNotNegative = isNotNegativeReply(score);
-            boolean isIrrelevant = isIrrelevantReply(replyItem);
-            boolean isHidden = replyItem.isRepeated() || replyItem.isConflicting() || isIrrelevant || isNotNegative;
-            if (!replyItem.isConflicting() && !isIrrelevant && score != null) {
-                log.debug("Score added: {}", score);
-                reviewScores.add(score);
-            }
-            if (changeSetData.getReplyFilterEnabled() && isHidden) {
-                continue;
-            }
-            if (changeSetData.getDebugReviewMode()) {
-                reply += debugCodeBlocksReview.getDebugCodeBlock(replyItem, isHidden);
-            }
-            ReviewBatch batchMap = new ReviewBatch(reply);
-            if (change.getIsCommentEvent() && replyItem.getId() != null) {
-                setCommentBatchMap(batchMap, replyItem.getId());
-            }
-            else {
-                setPatchSetReviewBatchMap(batchMap, replyItem);
-            }
-            reviewBatches.add(batchMap);
-        }
-    }
-
-    private AIChatResponseContent getReviewReply(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 new AIChatResponseContent(String.format(SPLIT_REVIEW_MSG, config.getMaxReviewLines()));
-        }
-
-        return chatGptClient.ask(changeSetData, change, patchSet);
-    }
-
-    private Integer getReviewScore(GerritChange change) {
-        if (config.isVotingEnabled()) {
-            return reviewScores.isEmpty() ?
-                    (change.getIsCommentEvent() ? null : 0) :
-                    Collections.min(reviewScores);
-        }
-        else {
-            return null;
-        }
-    }
-
-    private boolean isNotNegativeReply(Integer score) {
-        return score != null &&
-                config.getFilterNegativeComments() &&
-                score >= config.getFilterCommentsBelowScore();
-    }
-
-    private boolean isIrrelevantReply(AIChatReplyItem replyItem) {
-        return config.getFilterRelevantComments() &&
-                replyItem.getRelevance() != null &&
-                replyItem.getRelevance() < config.getFilterCommentsRelevanceThreshold();
-    }
+  private boolean isIrrelevantReply(AIChatReplyItem replyItem) {
+    return config.getFilterRelevantComments()
+        && replyItem.getRelevance() != null
+        && replyItem.getRelevance() < config.getFilterCommentsRelevanceThreshold();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/ConfigCreator.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/ConfigCreator.java
index fc22c23..0185c47 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/ConfigCreator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/ConfigCreator.java
@@ -1,12 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.config;
 
+import static com.googlesource.gerrit.plugins.aicodereview.config.DynamicConfiguration.KEY_DYNAMIC_CONFIG;
+
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.project.NoSuchProjectException;
@@ -14,111 +30,107 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerBaseProvider;
-import lombok.NonNull;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.jgit.lib.Config;
-
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-
-import static com.googlesource.gerrit.plugins.aicodereview.config.DynamicConfiguration.KEY_DYNAMIC_CONFIG;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.jgit.lib.Config;
 
 @Singleton
 @Slf4j
 public class ConfigCreator {
-    private final String pluginName;
+  private final String pluginName;
 
-    private final AccountCache accountCache;
-    private final PluginConfigFactory configFactory;
+  private final AccountCache accountCache;
+  private final PluginConfigFactory configFactory;
 
-    private final OneOffRequestContext context;
-    private final GerritApi gerritApi;
-    private final PluginDataHandlerBaseProvider pluginDataHandlerBaseProvider;
+  private final OneOffRequestContext context;
+  private final GerritApi gerritApi;
+  private final PluginDataHandlerBaseProvider pluginDataHandlerBaseProvider;
 
-    @Inject
-    ConfigCreator(
-            @PluginName String pluginName,
-            AccountCache accountCache,
-            PluginConfigFactory configFactory,
-            OneOffRequestContext context,
-            GerritApi gerritApi,
-            PluginDataHandlerBaseProvider pluginDataHandlerBaseProvider
-    ) {
-        this.pluginName = pluginName;
-        this.accountCache = accountCache;
-        this.configFactory = configFactory;
-        this.context = context;
-        this.gerritApi = gerritApi;
-        this.pluginDataHandlerBaseProvider = pluginDataHandlerBaseProvider;
+  @Inject
+  ConfigCreator(
+      @PluginName String pluginName,
+      AccountCache accountCache,
+      PluginConfigFactory configFactory,
+      OneOffRequestContext context,
+      GerritApi gerritApi,
+      PluginDataHandlerBaseProvider pluginDataHandlerBaseProvider) {
+    this.pluginName = pluginName;
+    this.accountCache = accountCache;
+    this.configFactory = configFactory;
+    this.context = context;
+    this.gerritApi = gerritApi;
+    this.pluginDataHandlerBaseProvider = pluginDataHandlerBaseProvider;
+  }
+
+  public Configuration createConfig(Project.NameKey projectName, Change.Key changeKey)
+      throws NoSuchProjectException {
+    PluginConfig globalConfig = configFactory.getFromGerritConfig(pluginName);
+    log.debug(
+        "These configuration items have been set in the global configuration: {}",
+        globalConfig.getNames());
+    PluginConfig projectConfig = configFactory.getFromProjectConfig(projectName, pluginName);
+    log.debug(
+        "These configuration items have been set in the project configuration: {}",
+        projectConfig.getNames());
+    // `PluginDataHandlerProvider` cannot be injected because `GerritChange` is not initialized at
+    // this stage:
+    // instead of using `PluginDataHandlerProvider.getChangeScope`,
+    // `PluginDataHandlerBaseProvider.get` is employed
+    Map<String, String> dynamicConfig =
+        pluginDataHandlerBaseProvider
+            .get(changeKey.toString())
+            .getJsonValue(KEY_DYNAMIC_CONFIG, String.class);
+    if (dynamicConfig != null && !dynamicConfig.isEmpty()) {
+      log.info("DynamicConfig found for change '{}': {}", changeKey, dynamicConfig);
+      projectConfig = updateDynamicConfig(projectConfig, pluginName, dynamicConfig);
     }
+    Optional<AccountState> codeReviewAccount = getAccount(globalConfig);
+    String email = codeReviewAccount.map(a -> a.account().preferredEmail()).orElse("");
+    Account.Id accountId =
+        codeReviewAccount
+            .map(a -> a.account().id())
+            .orElseThrow(
+                () ->
+                    new RuntimeException(
+                        String.format(
+                            "Given account %s doesn't exist",
+                            globalConfig.getString(Configuration.KEY_GERRIT_USERNAME))));
+    return new Configuration(context, gerritApi, globalConfig, projectConfig, email, accountId);
+  }
 
-    public Configuration createConfig(Project.NameKey projectName, Change.Key changeKey) throws NoSuchProjectException {
-        PluginConfig globalConfig = configFactory.getFromGerritConfig(pluginName);
-        log.debug(
-            "These configuration items have been set in the global configuration: {}",
-            globalConfig.getNames());
-        PluginConfig projectConfig = configFactory.getFromProjectConfig(projectName, pluginName);
-        log.debug(
-            "These configuration items have been set in the project configuration: {}",
-            projectConfig.getNames());
-        // `PluginDataHandlerProvider` cannot be injected because `GerritChange` is not initialized at this stage:
-        // instead of using `PluginDataHandlerProvider.getChangeScope`, `PluginDataHandlerBaseProvider.get` is employed
-        Map<String, String> dynamicConfig = pluginDataHandlerBaseProvider.get(changeKey.toString())
-                .getJsonValue(KEY_DYNAMIC_CONFIG, String.class);
-        if (dynamicConfig != null && !dynamicConfig.isEmpty()) {
-            log.info("DynamicConfig found for change '{}': {}", changeKey, dynamicConfig);
-            projectConfig = updateDynamicConfig(projectConfig, pluginName, dynamicConfig);
-        }
-        Optional<AccountState> codeReviewAccount = getAccount(globalConfig);
-        String email = codeReviewAccount.map(a -> a.account().preferredEmail()).orElse("");
-        Account.Id accountId =
-            codeReviewAccount
-                .map(a -> a.account().id())
-                .orElseThrow(
-                    () ->
-                        new RuntimeException(
-                            String.format(
-                                "Given account %s doesn't exist",
-                                globalConfig.getString(Configuration.KEY_GERRIT_USERNAME))));
-        return new Configuration(context, gerritApi, globalConfig, projectConfig, email, accountId);
+  private Optional<AccountState> getAccount(PluginConfig globalConfig) {
+    String codeReviewUser = globalConfig.getString(Configuration.KEY_GERRIT_USERNAME);
+    return accountCache.getByUsername(codeReviewUser);
+  }
+
+  private PluginConfig updateDynamicConfig(
+      PluginConfig projectConfig, String pluginName, Map<String, String> dynamicConfig) {
+    // Retrieve all current configuration values
+    Set<String> keys = projectConfig.getNames();
+    Map<String, String> currentConfigValues = new HashMap<>();
+    for (String key : keys) {
+      currentConfigValues.put(key, projectConfig.getString(key));
     }
+    // Merge current config with new values from dynamicConfig
+    currentConfigValues.putAll(dynamicConfig);
+    PluginConfig.Update configUpdater = getProjectUpdate(pluginName, currentConfigValues);
 
-    private Optional<AccountState> getAccount(PluginConfig globalConfig) {
-        String codeReviewUser = globalConfig.getString(Configuration.KEY_GERRIT_USERNAME);
-        return accountCache.getByUsername(codeReviewUser);
+    return configUpdater.asPluginConfig();
+  }
+
+  private PluginConfig.@NonNull Update getProjectUpdate(
+      String pluginName, Map<String, String> currentConfigValues) {
+    // Use PluginConfig.Update to apply merged configuration
+    PluginConfig.Update configUpdater =
+        new PluginConfig.Update(pluginName, new Config(), Optional.empty());
+    // Set all merged values
+    for (Map.Entry<String, String> entry : currentConfigValues.entrySet()) {
+      configUpdater.setString(entry.getKey(), entry.getValue());
     }
-
-    private PluginConfig updateDynamicConfig(
-            PluginConfig projectConfig,
-            String pluginName,
-            Map<String, String> dynamicConfig
-    ) {
-        // Retrieve all current configuration values
-        Set<String> keys = projectConfig.getNames();
-        Map<String, String> currentConfigValues = new HashMap<>();
-        for (String key : keys) {
-            currentConfigValues.put(key, projectConfig.getString(key));
-        }
-        // Merge current config with new values from dynamicConfig
-        currentConfigValues.putAll(dynamicConfig);
-        PluginConfig.Update configUpdater = getProjectUpdate(pluginName, currentConfigValues);
-
-        return configUpdater.asPluginConfig();
-    }
-
-    private PluginConfig.@NonNull Update getProjectUpdate(String pluginName, Map<String, String> currentConfigValues) {
-        // Use PluginConfig.Update to apply merged configuration
-        PluginConfig.Update configUpdater = new PluginConfig.Update(
-                pluginName,
-                new Config(),
-                Optional.empty()
-        );
-        // Set all merged values
-        for (Map.Entry<String, String> entry : currentConfigValues.entrySet()) {
-            configUpdater.setString(entry.getKey(), entry.getValue());
-        }
-        return configUpdater;
-    }
+    return configUpdater;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/Configuration.java
index c79a24b..40a9a79 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/Configuration.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.config;
 
 import com.google.common.base.Strings;
@@ -7,412 +21,404 @@
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
-
+import com.googlesource.gerrit.plugins.aicodereview.settings.Settings.AIType;
+import com.googlesource.gerrit.plugins.aicodereview.settings.Settings.Modes;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.NameValuePair;
 import org.apache.http.message.BasicNameValuePair;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.*;
-import java.util.regex.Pattern;
-
-import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.Modes;
-import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.AIType;
-
+import org.eclipse.jgit.annotations.NonNull;
 
 @Slf4j
 public class Configuration {
-    // Config Constants
-    public static final String ENABLED_USERS_ALL = "ALL";
-    public static final String ENABLED_GROUPS_ALL = "ALL";
-    public static final String ENABLED_TOPICS_ALL = "ALL";
-    public static final String NOT_CONFIGURED_ERROR_MSG = "%s is not configured";
+  // Config Constants
+  public static final String ENABLED_USERS_ALL = "ALL";
+  public static final String ENABLED_GROUPS_ALL = "ALL";
+  public static final String ENABLED_TOPICS_ALL = "ALL";
+  public static final String NOT_CONFIGURED_ERROR_MSG = "%s is not configured";
 
-    // Default Config values
-    public static final String OPENAI_DOMAIN = "https://api.openai.com";
-    public static final String DEFAULT_CHATGPT_MODEL = "gpt-4o";
-    public static final String DEFAULT_OPENAI_MODEL = "qwen2.5-coder";
+  // Default Config values
+  public static final String OPENAI_DOMAIN = "https://api.openai.com";
+  public static final String DEFAULT_CHATGPT_MODEL = "gpt-4o";
+  public static final String DEFAULT_OPENAI_MODEL = "qwen2.5-coder";
 
-    public static final double DEFAULT_AI_CHAT_REVIEW_TEMPERATURE = 0.2;
-    public static final double DEFAULT_AI_CHAT_COMMENT_TEMPERATURE = 1.0;
+  public static final double DEFAULT_AI_CHAT_REVIEW_TEMPERATURE = 0.2;
+  public static final double DEFAULT_AI_CHAT_COMMENT_TEMPERATURE = 1.0;
 
-    private static final String DEFAULT_AI_MODE = "stateless";
-    private static final boolean DEFAULT_REVIEW_PATCH_SET = true;
-    private static final boolean DEFAULT_REVIEW_COMMIT_MESSAGES = true;
-    private static final boolean DEFAULT_FULL_FILE_REVIEW = true;
-    private static final boolean DEFAULT_STREAM_OUTPUT = false;
-    private static final boolean DEFAULT_GLOBAL_ENABLE = false;
-    private static final String DEFAULT_DISABLED_USERS = "";
-    private static final String DEFAULT_ENABLED_USERS = ENABLED_USERS_ALL;
-    private static final String DEFAULT_DISABLED_GROUPS = "";
-    private static final String DEFAULT_ENABLED_GROUPS = ENABLED_GROUPS_ALL;
-    private static final String DEFAULT_DISABLED_TOPIC_FILTER = "";
-    private static final String DEFAULT_ENABLED_TOPIC_FILTER = ENABLED_TOPICS_ALL;
-    private static final String DEFAULT_ENABLED_PROJECTS = "";
-    private static final String DEFAULT_ENABLED_FILE_EXTENSIONS = String.join(",", new String[]{
-            ".py",
-            ".java",
-            ".js",
-            ".ts",
-            ".html",
-            ".css",
-            ".cs",
-            ".cpp",
-            ".c",
-            ".h",
-            ".php",
-            ".rb",
-            ".swift",
-            ".kt",
-            ".r",
-            ".jl",
-            ".go",
-            ".scala",
-            ".pl",
-            ".pm",
-            ".rs",
-            ".dart",
-            ".lua",
-            ".sh",
-            ".vb",
-            ".bat"
-    });
-    private static final boolean DEFAULT_PROJECT_ENABLE = false;
-    private static final int DEFAULT_MAX_REVIEW_LINES = 1000;
-    private static final int DEFAULT_MAX_REVIEW_FILE_SIZE = 10000;
-    private static final boolean DEFAULT_ENABLED_VOTING = false;
-    private static final boolean DEFAULT_FILTER_NEGATIVE_COMMENTS = true;
-    private static final int DEFAULT_FILTER_COMMENTS_BELOW_SCORE = 0;
-    private static final boolean DEFAULT_FILTER_RELEVANT_COMMENTS = true;
-    private static final double DEFAULT_FILTER_COMMENTS_RELEVANCE_THRESHOLD = 0.6;
-    private static final int DEFAULT_VOTING_MIN_SCORE = -1;
-    private static final int DEFAULT_VOTING_MAX_SCORE = 1;
-    private static final boolean DEFAULT_INLINE_COMMENTS_AS_RESOLVED = false;
-    private static final boolean DEFAULT_PATCH_SET_COMMENTS_AS_RESOLVED = false;
-    private static final boolean DEFAULT_IGNORE_OUTDATED_INLINE_COMMENTS = false;
-    private static final boolean DEFAULT_IGNORE_RESOLVED_AI_CHAT_COMMENTS = true;
-    private static final boolean DEFAULT_FORCE_CREATE_ASSISTANT = false;
-    private static final boolean DEFAULT_ENABLE_MESSAGE_DEBUGGING = false;
+  private static final String DEFAULT_AI_MODE = "stateless";
+  private static final boolean DEFAULT_REVIEW_PATCH_SET = true;
+  private static final boolean DEFAULT_REVIEW_COMMIT_MESSAGES = true;
+  private static final boolean DEFAULT_FULL_FILE_REVIEW = true;
+  private static final boolean DEFAULT_STREAM_OUTPUT = false;
+  private static final boolean DEFAULT_GLOBAL_ENABLE = false;
+  private static final String DEFAULT_DISABLED_USERS = "";
+  private static final String DEFAULT_ENABLED_USERS = ENABLED_USERS_ALL;
+  private static final String DEFAULT_DISABLED_GROUPS = "";
+  private static final String DEFAULT_ENABLED_GROUPS = ENABLED_GROUPS_ALL;
+  private static final String DEFAULT_DISABLED_TOPIC_FILTER = "";
+  private static final String DEFAULT_ENABLED_TOPIC_FILTER = ENABLED_TOPICS_ALL;
+  private static final String DEFAULT_ENABLED_PROJECTS = "";
+  private static final String DEFAULT_ENABLED_FILE_EXTENSIONS =
+      String.join(
+          ",",
+          new String[] {
+            ".py", ".java", ".js", ".ts", ".html", ".css", ".cs", ".cpp", ".c", ".h", ".php", ".rb",
+            ".swift", ".kt", ".r", ".jl", ".go", ".scala", ".pl", ".pm", ".rs", ".dart", ".lua",
+            ".sh", ".vb", ".bat"
+          });
+  private static final boolean DEFAULT_PROJECT_ENABLE = false;
+  private static final int DEFAULT_MAX_REVIEW_LINES = 1000;
+  private static final int DEFAULT_MAX_REVIEW_FILE_SIZE = 10000;
+  private static final boolean DEFAULT_ENABLED_VOTING = false;
+  private static final boolean DEFAULT_FILTER_NEGATIVE_COMMENTS = true;
+  private static final int DEFAULT_FILTER_COMMENTS_BELOW_SCORE = 0;
+  private static final boolean DEFAULT_FILTER_RELEVANT_COMMENTS = true;
+  private static final double DEFAULT_FILTER_COMMENTS_RELEVANCE_THRESHOLD = 0.6;
+  private static final int DEFAULT_VOTING_MIN_SCORE = -1;
+  private static final int DEFAULT_VOTING_MAX_SCORE = 1;
+  private static final boolean DEFAULT_INLINE_COMMENTS_AS_RESOLVED = false;
+  private static final boolean DEFAULT_PATCH_SET_COMMENTS_AS_RESOLVED = false;
+  private static final boolean DEFAULT_IGNORE_OUTDATED_INLINE_COMMENTS = false;
+  private static final boolean DEFAULT_IGNORE_RESOLVED_AI_CHAT_COMMENTS = true;
+  private static final boolean DEFAULT_FORCE_CREATE_ASSISTANT = false;
+  private static final boolean DEFAULT_ENABLE_MESSAGE_DEBUGGING = false;
 
-    public static final String AUTH_HEADER_API_KEY = "api-key";
+  public static final String AUTH_HEADER_API_KEY = "api-key";
 
-    // Config setting keys
-    public static final String KEY_AI_SYSTEM_PROMPT = "aiSystemPrompt";
-    public static final String KEY_AI_RELEVANCE_RULES = "aiRelevanceRules";
-    public static final String KEY_AI_REVIEW_TEMPERATURE = "aiReviewTemperature";
-    public static final String KEY_AI_COMMENT_TEMPERATURE = "aiCommentTemperature";
-    public static final String KEY_VOTING_MIN_SCORE = "votingMinScore";
-    public static final String KEY_VOTING_MAX_SCORE = "votingMaxScore";
-    public static final String KEY_GERRIT_USERNAME = "gerritUserName";
+  // Config setting keys
+  public static final String KEY_AI_SYSTEM_PROMPT = "aiSystemPrompt";
+  public static final String KEY_AI_RELEVANCE_RULES = "aiRelevanceRules";
+  public static final String KEY_AI_REVIEW_TEMPERATURE = "aiReviewTemperature";
+  public static final String KEY_AI_COMMENT_TEMPERATURE = "aiCommentTemperature";
+  public static final String KEY_VOTING_MIN_SCORE = "votingMinScore";
+  public static final String KEY_VOTING_MAX_SCORE = "votingMaxScore";
+  public static final String KEY_GERRIT_USERNAME = "gerritUserName";
 
-    public static final String KEY_AI_TYPE = "aiType";
-    public static final String KEY_AI_TOKEN = "aiToken";
-    public static final String KEY_AI_DOMAIN = "aiDomain";
-    public static final String KEY_AI_CHAT_ENDPOINT = "aiChatEndpoint";
-    public static final String KEY_AI_AUTH_HEADER_NAME = "aiAuthHeaderName";
-    private static final String KEY_AI_MODEL = "aiModel";
-    public static final String KEY_STREAM_OUTPUT = "aiStreamOutput";
-    private static final String KEY_AI_MODE = "aiMode";
-    private static final String KEY_REVIEW_COMMIT_MESSAGES = "aiReviewCommitMessages";
-    private static final String KEY_REVIEW_PATCH_SET = "aiReviewPatchSet";
-    private static final String KEY_FULL_FILE_REVIEW = "gptFullFileReview";
-    private static final String KEY_PROJECT_ENABLE = "isEnabled";
-    private static final String KEY_GLOBAL_ENABLE = "globalEnable";
-    private static final String KEY_DISABLED_USERS = "disabledUsers";
-    private static final String KEY_ENABLED_USERS = "enabledUsers";
-    private static final String KEY_DISABLED_GROUPS = "disabledGroups";
-    private static final String KEY_ENABLED_GROUPS = "enabledGroups";
-    private static final String KEY_DISABLED_TOPIC_FILTER = "disabledTopicFilter";
-    private static final String KEY_ENABLED_TOPIC_FILTER = "enabledTopicFilter";
-    private static final String KEY_ENABLED_PROJECTS = "enabledProjects";
-    private static final String KEY_MAX_REVIEW_LINES = "maxReviewLines";
-    private static final String KEY_MAX_REVIEW_FILE_SIZE = "maxReviewFileSize";
-    private static final String KEY_ENABLED_FILE_EXTENSIONS = "enabledFileExtensions";
-    private static final String KEY_ENABLED_VOTING = "enabledVoting";
-    private static final String KEY_FILTER_NEGATIVE_COMMENTS = "filterNegativeComments";
-    private static final String KEY_FILTER_COMMENTS_BELOW_SCORE = "filterCommentsBelowScore";
-    private static final String KEY_FILTER_RELEVANT_COMMENTS = "filterRelevantComments";
-    private static final String KEY_FILTER_COMMENTS_RELEVANCE_THRESHOLD = "filterCommentsRelevanceThreshold";
-    private static final String KEY_INLINE_COMMENTS_AS_RESOLVED = "inlineCommentsAsResolved";
-    private static final String KEY_PATCH_SET_COMMENTS_AS_RESOLVED = "patchSetCommentsAsResolved";
-    private static final String KEY_IGNORE_OUTDATED_INLINE_COMMENTS = "ignoreOutdatedInlineComments";
-    private static final String KEY_IGNORE_RESOLVED_AI_CHAT_COMMENTS = "ignoreResolvedChatGptComments";
-    private static final String KEY_FORCE_CREATE_ASSISTANT = "forceCreateAssistant";
-    private static final String KEY_ENABLE_MESSAGE_DEBUGGING = "enableMessageDebugging";
+  public static final String KEY_AI_TYPE = "aiType";
+  public static final String KEY_AI_TOKEN = "aiToken";
+  public static final String KEY_AI_DOMAIN = "aiDomain";
+  public static final String KEY_AI_CHAT_ENDPOINT = "aiChatEndpoint";
+  public static final String KEY_AI_AUTH_HEADER_NAME = "aiAuthHeaderName";
+  private static final String KEY_AI_MODEL = "aiModel";
+  public static final String KEY_STREAM_OUTPUT = "aiStreamOutput";
+  private static final String KEY_AI_MODE = "aiMode";
+  private static final String KEY_REVIEW_COMMIT_MESSAGES = "aiReviewCommitMessages";
+  private static final String KEY_REVIEW_PATCH_SET = "aiReviewPatchSet";
+  private static final String KEY_FULL_FILE_REVIEW = "gptFullFileReview";
+  private static final String KEY_PROJECT_ENABLE = "isEnabled";
+  private static final String KEY_GLOBAL_ENABLE = "globalEnable";
+  private static final String KEY_DISABLED_USERS = "disabledUsers";
+  private static final String KEY_ENABLED_USERS = "enabledUsers";
+  private static final String KEY_DISABLED_GROUPS = "disabledGroups";
+  private static final String KEY_ENABLED_GROUPS = "enabledGroups";
+  private static final String KEY_DISABLED_TOPIC_FILTER = "disabledTopicFilter";
+  private static final String KEY_ENABLED_TOPIC_FILTER = "enabledTopicFilter";
+  private static final String KEY_ENABLED_PROJECTS = "enabledProjects";
+  private static final String KEY_MAX_REVIEW_LINES = "maxReviewLines";
+  private static final String KEY_MAX_REVIEW_FILE_SIZE = "maxReviewFileSize";
+  private static final String KEY_ENABLED_FILE_EXTENSIONS = "enabledFileExtensions";
+  private static final String KEY_ENABLED_VOTING = "enabledVoting";
+  private static final String KEY_FILTER_NEGATIVE_COMMENTS = "filterNegativeComments";
+  private static final String KEY_FILTER_COMMENTS_BELOW_SCORE = "filterCommentsBelowScore";
+  private static final String KEY_FILTER_RELEVANT_COMMENTS = "filterRelevantComments";
+  private static final String KEY_FILTER_COMMENTS_RELEVANCE_THRESHOLD =
+      "filterCommentsRelevanceThreshold";
+  private static final String KEY_INLINE_COMMENTS_AS_RESOLVED = "inlineCommentsAsResolved";
+  private static final String KEY_PATCH_SET_COMMENTS_AS_RESOLVED = "patchSetCommentsAsResolved";
+  private static final String KEY_IGNORE_OUTDATED_INLINE_COMMENTS = "ignoreOutdatedInlineComments";
+  private static final String KEY_IGNORE_RESOLVED_AI_CHAT_COMMENTS =
+      "ignoreResolvedChatGptComments";
+  private static final String KEY_FORCE_CREATE_ASSISTANT = "forceCreateAssistant";
+  private static final String KEY_ENABLE_MESSAGE_DEBUGGING = "enableMessageDebugging";
 
-    private final OneOffRequestContext context;
-    @Getter
-    private final Account.Id userId;
-    @Getter
-    private final PluginConfig globalConfig;
-    @Getter
-    private final PluginConfig projectConfig;
-    @Getter
-    private final String gerritUserEmail;
-    @Getter
-    private final GerritApi gerritApi;
+  private final OneOffRequestContext context;
+  @Getter private final Account.Id userId;
+  @Getter private final PluginConfig globalConfig;
+  @Getter private final PluginConfig projectConfig;
+  @Getter private final String gerritUserEmail;
+  @Getter private final GerritApi gerritApi;
 
+  public Configuration(
+      OneOffRequestContext context,
+      GerritApi gerritApi,
+      PluginConfig globalConfig,
+      PluginConfig projectConfig,
+      String gerritUserEmail,
+      Account.Id userId) {
+    this.context = context;
+    this.gerritApi = gerritApi;
+    this.globalConfig = globalConfig;
+    this.projectConfig = projectConfig;
+    this.gerritUserEmail = gerritUserEmail;
+    this.userId = userId;
+  }
 
-    public Configuration(OneOffRequestContext context, GerritApi gerritApi, PluginConfig globalConfig, PluginConfig projectConfig, String gerritUserEmail, Account.Id userId) {
-        this.context = context;
-        this.gerritApi = gerritApi;
-        this.globalConfig = globalConfig;
-        this.projectConfig = projectConfig;
-        this.gerritUserEmail = gerritUserEmail;
-        this.userId = userId;
+  public ManualRequestContext openRequestContext() {
+    return context.openAs(userId);
+  }
+
+  public String getAIToken() {
+    // The Aitoken isn't required for all aiTypes.
+
+    return getValidatedOrThrow(KEY_AI_TOKEN);
+  }
+
+  public String getGerritUserName() {
+    return getValidatedOrThrow(KEY_GERRIT_USERNAME);
+  }
+
+  public String getAIDomain() {
+    // AiDomain is not a required field UNLESS we are using OLLAMA which is a local / private based
+    // instance by its very nature. it makes more sense to enforce that it is a required field for
+    // when
+    // aiType==ollama.
+    String aiDomain =
+        getAIType() == AIType.OLLAMA
+            ? getValidatedOrThrow(KEY_AI_DOMAIN)
+            : getString(KEY_AI_DOMAIN, OPENAI_DOMAIN);
+
+    // trim end slash, so putting endpoint urls together is easier.
+    return aiDomain.endsWith("/") ? aiDomain.substring(0, aiDomain.length() - 1) : aiDomain;
+  }
+
+  public String getAIModel() {
+    // default to the chatGPT model if nothing is specified, excecpt if we are using
+    // an openAI compliant service like OLLAMA, then we use its appropriate default.
+    return getString(
+        KEY_AI_MODEL, getAIType() == AIType.OLLAMA ? DEFAULT_OPENAI_MODEL : DEFAULT_CHATGPT_MODEL);
+  }
+
+  public boolean getAIReviewPatchSet() {
+    return getBoolean(KEY_REVIEW_PATCH_SET, DEFAULT_REVIEW_PATCH_SET);
+  }
+
+  public Modes getAIMode() {
+    String mode = getString(KEY_AI_MODE, DEFAULT_AI_MODE);
+    return getValueAsEnum(Modes.class, mode);
+  }
+
+  public AIType getAIType() {
+    // return default type of CHATGPT if no value has been specified.
+    // Leaving the default behaviour of this plugin as it was historically.
+    String aiType = getString(KEY_AI_TYPE, "CHATGPT");
+    // for ease of use with enum, use toUpper, so we can always be case-sensitive on compares.
+    return getValueAsEnum(AIType.class, aiType.toUpperCase());
+  }
+
+  public String getChatEndpoint() {
+    // optional, and only used when combined with the "GENERIC" aiType, for testing
+    // of new or not yet supported ai frameworks.
+    return getString(KEY_AI_CHAT_ENDPOINT, "");
+  }
+
+  public String getAuthHeaderName() {
+    // optional, and only used when combined with the "GENERIC" aiType, for testing
+    // of new or not yet supported ai frameworks.
+    return getString(KEY_AI_AUTH_HEADER_NAME, "");
+  }
+
+  public boolean getAIReviewCommitMessages() {
+    return getBoolean(KEY_REVIEW_COMMIT_MESSAGES, DEFAULT_REVIEW_COMMIT_MESSAGES);
+  }
+
+  public boolean getGptFullFileReview() {
+    return getBoolean(KEY_FULL_FILE_REVIEW, DEFAULT_FULL_FILE_REVIEW);
+  }
+
+  public boolean getAIStreamOutput() {
+    return getBoolean(KEY_STREAM_OUTPUT, DEFAULT_STREAM_OUTPUT);
+  }
+
+  public boolean isProjectEnable() {
+    return projectConfig.getBoolean(KEY_PROJECT_ENABLE, DEFAULT_PROJECT_ENABLE);
+  }
+
+  public boolean isGlobalEnable() {
+    return globalConfig.getBoolean(KEY_GLOBAL_ENABLE, DEFAULT_GLOBAL_ENABLE);
+  }
+
+  public List<String> getDisabledUsers() {
+    return splitConfig(globalConfig.getString(KEY_DISABLED_USERS, DEFAULT_DISABLED_USERS));
+  }
+
+  public List<String> getEnabledUsers() {
+    return splitConfig(globalConfig.getString(KEY_ENABLED_USERS, DEFAULT_ENABLED_USERS));
+  }
+
+  public List<String> getDisabledGroups() {
+    return splitConfig(globalConfig.getString(KEY_DISABLED_GROUPS, DEFAULT_DISABLED_GROUPS));
+  }
+
+  public List<String> getEnabledGroups() {
+    return splitConfig(globalConfig.getString(KEY_ENABLED_GROUPS, DEFAULT_ENABLED_GROUPS));
+  }
+
+  public List<String> getDisabledTopicFilter() {
+    return splitConfig(
+        globalConfig.getString(KEY_DISABLED_TOPIC_FILTER, DEFAULT_DISABLED_TOPIC_FILTER));
+  }
+
+  public List<String> getEnabledTopicFilter() {
+    return splitConfig(
+        globalConfig.getString(KEY_ENABLED_TOPIC_FILTER, DEFAULT_ENABLED_TOPIC_FILTER));
+  }
+
+  public String getEnabledProjects() {
+    return globalConfig.getString(KEY_ENABLED_PROJECTS, DEFAULT_ENABLED_PROJECTS);
+  }
+
+  public int getMaxReviewLines() {
+    return getInt(KEY_MAX_REVIEW_LINES, DEFAULT_MAX_REVIEW_LINES);
+  }
+
+  public int getMaxReviewFileSize() {
+    return getInt(KEY_MAX_REVIEW_FILE_SIZE, DEFAULT_MAX_REVIEW_FILE_SIZE);
+  }
+
+  public List<String> getEnabledFileExtensions() {
+    return splitConfig(
+        globalConfig.getString(KEY_ENABLED_FILE_EXTENSIONS, DEFAULT_ENABLED_FILE_EXTENSIONS));
+  }
+
+  public boolean isVotingEnabled() {
+    return getBoolean(KEY_ENABLED_VOTING, DEFAULT_ENABLED_VOTING);
+  }
+
+  public boolean getFilterNegativeComments() {
+    return getBoolean(KEY_FILTER_NEGATIVE_COMMENTS, DEFAULT_FILTER_NEGATIVE_COMMENTS);
+  }
+
+  public int getFilterCommentsBelowScore() {
+    return getInt(KEY_FILTER_COMMENTS_BELOW_SCORE, DEFAULT_FILTER_COMMENTS_BELOW_SCORE);
+  }
+
+  public boolean getFilterRelevantComments() {
+    return getBoolean(KEY_FILTER_RELEVANT_COMMENTS, DEFAULT_FILTER_RELEVANT_COMMENTS);
+  }
+
+  public double getFilterCommentsRelevanceThreshold() {
+    return getDouble(
+        KEY_FILTER_COMMENTS_RELEVANCE_THRESHOLD, DEFAULT_FILTER_COMMENTS_RELEVANCE_THRESHOLD);
+  }
+
+  public Locale getLocaleDefault() {
+    return Locale.getDefault();
+  }
+
+  public int getVotingMinScore() {
+    return getInt(KEY_VOTING_MIN_SCORE, DEFAULT_VOTING_MIN_SCORE);
+  }
+
+  public int getVotingMaxScore() {
+    return getInt(KEY_VOTING_MAX_SCORE, DEFAULT_VOTING_MAX_SCORE);
+  }
+
+  public boolean getInlineCommentsAsResolved() {
+    return getBoolean(KEY_INLINE_COMMENTS_AS_RESOLVED, DEFAULT_INLINE_COMMENTS_AS_RESOLVED);
+  }
+
+  public boolean getPatchSetCommentsAsResolved() {
+    return getBoolean(KEY_PATCH_SET_COMMENTS_AS_RESOLVED, DEFAULT_PATCH_SET_COMMENTS_AS_RESOLVED);
+  }
+
+  public boolean getIgnoreResolvedAIChatComments() {
+    return getBoolean(
+        KEY_IGNORE_RESOLVED_AI_CHAT_COMMENTS, DEFAULT_IGNORE_RESOLVED_AI_CHAT_COMMENTS);
+  }
+
+  public boolean getForceCreateAssistant() {
+    return getBoolean(KEY_FORCE_CREATE_ASSISTANT, DEFAULT_FORCE_CREATE_ASSISTANT);
+  }
+
+  public boolean getEnableMessageDebugging() {
+    return getBoolean(KEY_ENABLE_MESSAGE_DEBUGGING, DEFAULT_ENABLE_MESSAGE_DEBUGGING);
+  }
+
+  public boolean getIgnoreOutdatedInlineComments() {
+    return getBoolean(KEY_IGNORE_OUTDATED_INLINE_COMMENTS, DEFAULT_IGNORE_OUTDATED_INLINE_COMMENTS);
+  }
+
+  public NameValuePair getAuthorizationHeaderInfo() {
+    switch (getAIType()) {
+      case AZUREOPENAI:
+        return new BasicNameValuePair(AUTH_HEADER_API_KEY, getAIToken());
+      case OLLAMA:
+      case GENERIC:
+        // by default no auth header is required for ollama so return null for no auth.
+        // But if they wish to add some auth requirements, maybe for hosted setup,
+        // then allow the header to be generically specified, same as the GENERIC configuration.
+        return !Strings.isNullOrEmpty(getAuthHeaderName())
+            ? new BasicNameValuePair(getAuthHeaderName(), getAIToken())
+            : null;
+      case CHATGPT:
+      default:
+        // by default, or for chatGpt use bearer token, if someone is adding a new aiType, it can
+        // fall
+        // into this block - or they will need to extend the cases above.
+        return new BasicNameValuePair(HttpHeaders.AUTHORIZATION, "Bearer " + getAIToken());
     }
+  }
 
-    public ManualRequestContext openRequestContext() {
-      return context.openAs(userId);
+  public String getString(String key, String defaultValue) {
+    String value = projectConfig.getString(key);
+    if (value != null) {
+      return value;
     }
+    return globalConfig.getString(key, defaultValue);
+  }
 
-    public String getAIToken() {
-        // The Aitoken isn't required for all aiTypes.
-
-        return getValidatedOrThrow(KEY_AI_TOKEN);
+  private String getValidatedOrThrow(String key) {
+    String value = projectConfig.getString(key);
+    if (value == null) {
+      value = globalConfig.getString(key);
     }
-
-    public String getGerritUserName() {
-        return getValidatedOrThrow(KEY_GERRIT_USERNAME);
+    if (value == null) {
+      throw new RuntimeException(String.format(NOT_CONFIGURED_ERROR_MSG, key));
     }
+    return value;
+  }
 
-    public String getAIDomain() {
-        // AiDomain is not a required field UNLESS we are using OLLAMA which is a local / private based
-        // instance by its very nature. it makes more sense to enforce that it is a required field for when
-        // aiType==ollama.
-        String aiDomain = getAIType() == AIType.OLLAMA ?
-                getValidatedOrThrow(KEY_AI_DOMAIN) :
-                getString(KEY_AI_DOMAIN, OPENAI_DOMAIN);
+  private int getInt(String key, int defaultValue) {
+    int valueForProject = projectConfig.getInt(key, defaultValue);
+    // To avoid misinterpreting an undefined value as zero, a secondary check is performed by
+    // retrieving the value
+    // as a String.
+    if (valueForProject != defaultValue
+        && valueForProject != 0
+        && projectConfig.getString(key, "") != null) {
+      return valueForProject;
+    }
+    return globalConfig.getInt(key, defaultValue);
+  }
 
-        // trim end slash, so putting endpoint urls together is easier.
-        return aiDomain.endsWith("/") ?
-                aiDomain.substring(0, aiDomain.length() - 1) : aiDomain;
+  private boolean getBoolean(String key, boolean defaultValue) {
+    boolean valueForProject = projectConfig.getBoolean(key, defaultValue);
+    if (projectConfig.getString(key) != null) {
+      return valueForProject;
     }
+    return globalConfig.getBoolean(key, defaultValue);
+  }
 
-    public String getAIModel() {
-        // default to the chatGPT model if nothing is specified, excecpt if we are using
-        // an openAI compliant service like OLLAMA, then we use its appropriate default.
-        return getString(KEY_AI_MODEL,
-                getAIType() == AIType.OLLAMA ? DEFAULT_OPENAI_MODEL : DEFAULT_CHATGPT_MODEL);
-    }
+  private Double getDouble(String key, Double defaultValue) {
+    return Double.parseDouble(getString(key, String.valueOf(defaultValue)));
+  }
 
-    public boolean getAIReviewPatchSet() {
-        return getBoolean(KEY_REVIEW_PATCH_SET, DEFAULT_REVIEW_PATCH_SET);
+  @NonNull
+  private static <T extends Enum<T>> T getValueAsEnum(Class<T> enumClass, String value) {
+    try {
+      return Enum.valueOf(enumClass, value);
+    } catch (IllegalArgumentException e) {
+      throw new RuntimeException(
+          String.format("Illegal value: %s for enum class: %s", value, enumClass), e);
     }
+  }
 
-    public Modes getAIMode() {
-        String mode = getString(KEY_AI_MODE, DEFAULT_AI_MODE);
-        return getValueAsEnum(Modes.class, mode);
-    }
-    public AIType getAIType(){
-        // return default type of CHATGPT if no value has been specified.
-        // Leaving the default behaviour of this plugin as it was historically.
-        String aiType = getString(KEY_AI_TYPE, "CHATGPT");
-        // for ease of use with enum, use toUpper, so we can always be case-sensitive on compares.
-        return getValueAsEnum( AIType.class, aiType.toUpperCase());
-    }
-
-    public String getChatEndpoint(){
-        // optional, and only used when combined with the "GENERIC" aiType, for testing
-        // of new or not yet supported ai frameworks.
-        return getString(KEY_AI_CHAT_ENDPOINT, "");
-    }
-    public String getAuthHeaderName(){
-        // optional, and only used when combined with the "GENERIC" aiType, for testing
-        // of new or not yet supported ai frameworks.
-        return getString(KEY_AI_AUTH_HEADER_NAME, "");
-    }
-
-    public boolean getAIReviewCommitMessages() {
-        return getBoolean(KEY_REVIEW_COMMIT_MESSAGES, DEFAULT_REVIEW_COMMIT_MESSAGES);
-    }
-
-    public boolean getGptFullFileReview() {
-        return getBoolean(KEY_FULL_FILE_REVIEW, DEFAULT_FULL_FILE_REVIEW);
-    }
-
-    public boolean getAIStreamOutput() {
-        return getBoolean(KEY_STREAM_OUTPUT, DEFAULT_STREAM_OUTPUT);
-    }
-
-    public boolean isProjectEnable() {
-        return projectConfig.getBoolean(KEY_PROJECT_ENABLE, DEFAULT_PROJECT_ENABLE);
-    }
-
-    public boolean isGlobalEnable() {
-        return globalConfig.getBoolean(KEY_GLOBAL_ENABLE, DEFAULT_GLOBAL_ENABLE);
-    }
-
-    public List<String> getDisabledUsers() {
-        return splitConfig(globalConfig.getString(KEY_DISABLED_USERS, DEFAULT_DISABLED_USERS));
-    }
-
-    public List<String> getEnabledUsers() {
-        return splitConfig(globalConfig.getString(KEY_ENABLED_USERS, DEFAULT_ENABLED_USERS));
-    }
-
-    public List<String> getDisabledGroups() {
-        return splitConfig(globalConfig.getString(KEY_DISABLED_GROUPS, DEFAULT_DISABLED_GROUPS));
-    }
-
-    public List<String> getEnabledGroups() {
-        return splitConfig(globalConfig.getString(KEY_ENABLED_GROUPS, DEFAULT_ENABLED_GROUPS));
-    }
-
-    public List<String> getDisabledTopicFilter() {
-        return splitConfig(globalConfig.getString(KEY_DISABLED_TOPIC_FILTER, DEFAULT_DISABLED_TOPIC_FILTER));
-    }
-
-    public List<String> getEnabledTopicFilter() {
-        return splitConfig(globalConfig.getString(KEY_ENABLED_TOPIC_FILTER, DEFAULT_ENABLED_TOPIC_FILTER));
-    }
-
-    public String getEnabledProjects() {
-        return globalConfig.getString(KEY_ENABLED_PROJECTS, DEFAULT_ENABLED_PROJECTS);
-    }
-
-    public int getMaxReviewLines() {
-        return getInt(KEY_MAX_REVIEW_LINES, DEFAULT_MAX_REVIEW_LINES);
-    }
-
-    public int getMaxReviewFileSize() {
-        return getInt(KEY_MAX_REVIEW_FILE_SIZE, DEFAULT_MAX_REVIEW_FILE_SIZE);
-    }
-
-    public List<String> getEnabledFileExtensions() {
-        return splitConfig(globalConfig.getString(KEY_ENABLED_FILE_EXTENSIONS, DEFAULT_ENABLED_FILE_EXTENSIONS));
-    }
-
-    public boolean isVotingEnabled() {
-        return getBoolean(KEY_ENABLED_VOTING, DEFAULT_ENABLED_VOTING);
-    }
-
-    public boolean getFilterNegativeComments() {
-        return getBoolean(KEY_FILTER_NEGATIVE_COMMENTS, DEFAULT_FILTER_NEGATIVE_COMMENTS);
-    }
-
-    public int getFilterCommentsBelowScore() {
-        return getInt(KEY_FILTER_COMMENTS_BELOW_SCORE, DEFAULT_FILTER_COMMENTS_BELOW_SCORE);
-    }
-
-    public boolean getFilterRelevantComments() {
-        return getBoolean(KEY_FILTER_RELEVANT_COMMENTS, DEFAULT_FILTER_RELEVANT_COMMENTS);
-    }
-
-    public double getFilterCommentsRelevanceThreshold() {
-        return getDouble(KEY_FILTER_COMMENTS_RELEVANCE_THRESHOLD, DEFAULT_FILTER_COMMENTS_RELEVANCE_THRESHOLD);
-    }
-
-    public Locale getLocaleDefault() {
-        return Locale.getDefault();
-    }
-
-    public int getVotingMinScore() {
-        return getInt(KEY_VOTING_MIN_SCORE, DEFAULT_VOTING_MIN_SCORE);
-    }
-
-    public int getVotingMaxScore() {
-        return getInt(KEY_VOTING_MAX_SCORE, DEFAULT_VOTING_MAX_SCORE);
-    }
-
-    public boolean getInlineCommentsAsResolved() {
-        return getBoolean(KEY_INLINE_COMMENTS_AS_RESOLVED, DEFAULT_INLINE_COMMENTS_AS_RESOLVED);
-    }
-
-    public boolean getPatchSetCommentsAsResolved() {
-        return getBoolean(KEY_PATCH_SET_COMMENTS_AS_RESOLVED, DEFAULT_PATCH_SET_COMMENTS_AS_RESOLVED);
-    }
-
-    public boolean getIgnoreResolvedAIChatComments() {
-        return getBoolean(KEY_IGNORE_RESOLVED_AI_CHAT_COMMENTS, DEFAULT_IGNORE_RESOLVED_AI_CHAT_COMMENTS);
-    }
-
-    public boolean getForceCreateAssistant() {
-        return getBoolean(KEY_FORCE_CREATE_ASSISTANT, DEFAULT_FORCE_CREATE_ASSISTANT);
-    }
-
-    public boolean getEnableMessageDebugging() {
-        return getBoolean(KEY_ENABLE_MESSAGE_DEBUGGING, DEFAULT_ENABLE_MESSAGE_DEBUGGING);
-    }
-
-    public boolean getIgnoreOutdatedInlineComments() {
-        return getBoolean(KEY_IGNORE_OUTDATED_INLINE_COMMENTS, DEFAULT_IGNORE_OUTDATED_INLINE_COMMENTS);
-    }
-
-
-    public NameValuePair getAuthorizationHeaderInfo() {
-        switch (getAIType()) {
-            case AZUREOPENAI:
-                return new BasicNameValuePair(AUTH_HEADER_API_KEY, getAIToken());
-            case OLLAMA:
-            case GENERIC:
-                // by default no auth header is required for ollama so return null for no auth.
-                // But if they wish to add some auth requirements, maybe for hosted setup,
-                // then allow the header to be generically specified, same as the GENERIC configuration.
-                return !Strings.isNullOrEmpty(getAuthHeaderName()) ?
-                        new BasicNameValuePair(getAuthHeaderName(), getAIToken()) : null;
-            case CHATGPT:
-            default:
-                // by default, or for chatGpt use bearer token, if someone is adding a new aiType, it can fall
-                // into this block - or they will need to extend the cases above.
-                return new BasicNameValuePair(HttpHeaders.AUTHORIZATION, "Bearer " + getAIToken());
-        }
-    }
-
-    public String getString(String key, String defaultValue) {
-        String value = projectConfig.getString(key);
-        if (value != null) {
-            return value;
-        }
-        return globalConfig.getString(key, defaultValue);
-    }
-
-    private String getValidatedOrThrow(String key) {
-        String value = projectConfig.getString(key);
-        if (value == null) {
-            value = globalConfig.getString(key);
-        }
-        if (value == null) {
-            throw new RuntimeException(String.format(NOT_CONFIGURED_ERROR_MSG, key));
-        }
-        return value;
-    }
-
-    private int getInt(String key, int defaultValue) {
-        int valueForProject = projectConfig.getInt(key, defaultValue);
-        // To avoid misinterpreting an undefined value as zero, a secondary check is performed by retrieving the value
-        // as a String.
-        if (valueForProject != defaultValue && valueForProject != 0
-                && projectConfig.getString(key, "") != null) {
-            return valueForProject;
-        }
-        return globalConfig.getInt(key, defaultValue);
-    }
-
-    private boolean getBoolean(String key, boolean defaultValue) {
-        boolean valueForProject = projectConfig.getBoolean(key, defaultValue);
-        if (projectConfig.getString(key) != null) {
-            return valueForProject;
-        }
-        return globalConfig.getBoolean(key, defaultValue);
-    }
-
-    private Double getDouble(String key, Double defaultValue) {
-        return Double.parseDouble(getString(key, String.valueOf(defaultValue)));
-    }
-
-    @NotNull
-    private static <T extends Enum<T>> T getValueAsEnum(Class<T> enumClass, String value) {
-        try {
-            return Enum.valueOf(enumClass, value);
-        } catch (IllegalArgumentException e) {
-            throw new RuntimeException(String.format("Illegal value: %s for enum class: %s", value, enumClass), e);
-        }
-    }
-
-    private List<String> splitConfig(String value) {
-        Pattern separator=Pattern.compile("\\s*,\\s*");
-        return Arrays.asList(separator.split(value));
-    }
+  private List<String> splitConfig(String value) {
+    Pattern separator = Pattern.compile("\\s*,\\s*");
+    return Arrays.asList(separator.split(value));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/DynamicConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/DynamicConfiguration.java
index 398da82..e7ca9f3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/DynamicConfiguration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/config/DynamicConfiguration.java
@@ -1,48 +1,62 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.config;
 
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class DynamicConfiguration {
-    public static final String KEY_DYNAMIC_CONFIG = "dynamicConfig";
+  public static final String KEY_DYNAMIC_CONFIG = "dynamicConfig";
 
-    private final PluginDataHandler pluginDataHandler;
-    @Getter
-    private final Map<String, String> dynamicConfig;
+  private final PluginDataHandler pluginDataHandler;
+  @Getter private final Map<String, String> dynamicConfig;
 
-    public DynamicConfiguration(PluginDataHandlerProvider pluginDataHandlerProvider) {
-        this.pluginDataHandler = pluginDataHandlerProvider.getChangeScope();
-        dynamicConfig = Optional.ofNullable(pluginDataHandler.getJsonValue(KEY_DYNAMIC_CONFIG, String.class))
-                .orElse(new HashMap<>());
+  public DynamicConfiguration(PluginDataHandlerProvider pluginDataHandlerProvider) {
+    this.pluginDataHandler = pluginDataHandlerProvider.getChangeScope();
+    dynamicConfig =
+        Optional.ofNullable(pluginDataHandler.getJsonValue(KEY_DYNAMIC_CONFIG, String.class))
+            .orElse(new HashMap<>());
+  }
+
+  public void setConfig(String key, String value) {
+    dynamicConfig.put(key, value);
+  }
+
+  public void updateConfiguration(boolean modifiedDynamicConfig, boolean shouldResetDynamicConfig) {
+    if (dynamicConfig == null || dynamicConfig.isEmpty()) return;
+    if (shouldResetDynamicConfig && !modifiedDynamicConfig) {
+      pluginDataHandler.removeValue(KEY_DYNAMIC_CONFIG);
+    } else {
+      if (shouldResetDynamicConfig) {
+        resetDynamicConfig();
+      }
+      log.info("Updating dynamic configuration with {}", dynamicConfig);
+      pluginDataHandler.setJsonValue(KEY_DYNAMIC_CONFIG, dynamicConfig);
     }
+  }
 
-    public void setConfig(String key, String value) {
-        dynamicConfig.put(key, value);
-    }
-
-    public void updateConfiguration(boolean modifiedDynamicConfig, boolean shouldResetDynamicConfig) {
-        if (dynamicConfig == null || dynamicConfig.isEmpty()) return;
-        if (shouldResetDynamicConfig && !modifiedDynamicConfig) {
-            pluginDataHandler.removeValue(KEY_DYNAMIC_CONFIG);
-        }
-        else {
-            if (shouldResetDynamicConfig) {
-                resetDynamicConfig();
-            }
-            log.info("Updating dynamic configuration with {}", dynamicConfig);
-            pluginDataHandler.setJsonValue(KEY_DYNAMIC_CONFIG, dynamicConfig);
-        }
-    }
-
-    private void resetDynamicConfig() {
-        // The keys with empty values are simply removed
-        dynamicConfig.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().isEmpty());
-    }
+  private void resetDynamicConfig() {
+    // The keys with empty values are simply removed
+    dynamicConfig
+        .entrySet()
+        .removeIf(entry -> entry.getValue() == null || entry.getValue().isEmpty());
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataHandler.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataHandler.java
index af6bb68..514d3ae 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataHandler.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.data;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -8,43 +22,38 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPermittedVotingRange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.HashSet;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class ChangeSetDataHandler {
-    public static void update(
-            Configuration config,
-            GerritChange change,
-            GerritClient gerritClient,
-            ChangeSetData changeSetData,
-            Localizer localizer
-    ) {
-        GerritClientData gerritClientData = gerritClient.getClientData(change);
-        AIChatDataPrompt AIChatDataPrompt = new AIChatDataPrompt(
-                config,
-                changeSetData,
-                change,
-                gerritClientData,
-                localizer);
+  public static void update(
+      Configuration config,
+      GerritChange change,
+      GerritClient gerritClient,
+      ChangeSetData changeSetData,
+      Localizer localizer) {
+    GerritClientData gerritClientData = gerritClient.getClientData(change);
+    AIChatDataPrompt AIChatDataPrompt =
+        new AIChatDataPrompt(config, changeSetData, change, gerritClientData, localizer);
 
-        changeSetData.setCommentPropertiesSize(gerritClientData.getCommentProperties().size());
-        changeSetData.setDirectives(new HashSet<>());
-        changeSetData.setReviewSystemMessage(null);
-        changeSetData.setReviewAIDataPrompt(AIChatDataPrompt.buildPrompt());
-        if (config.isVotingEnabled() && !change.getIsCommentEvent()) {
-            GerritPermittedVotingRange permittedVotingRange = gerritClient.getPermittedVotingRange(change);
-            if (permittedVotingRange != null) {
-                if (permittedVotingRange.getMin() > config.getVotingMinScore()) {
-                    log.debug("Minimum ChatGPT voting score set to {}", permittedVotingRange.getMin());
-                    changeSetData.setVotingMinScore(permittedVotingRange.getMin());
-                }
-                if (permittedVotingRange.getMax() < config.getVotingMaxScore()) {
-                    log.debug("Maximum ChatGPT voting score set to {}", permittedVotingRange.getMax());
-                    changeSetData.setVotingMaxScore(permittedVotingRange.getMax());
-                }
-            }
+    changeSetData.setCommentPropertiesSize(gerritClientData.getCommentProperties().size());
+    changeSetData.setDirectives(new HashSet<>());
+    changeSetData.setReviewSystemMessage(null);
+    changeSetData.setReviewAIDataPrompt(AIChatDataPrompt.buildPrompt());
+    if (config.isVotingEnabled() && !change.getIsCommentEvent()) {
+      GerritPermittedVotingRange permittedVotingRange =
+          gerritClient.getPermittedVotingRange(change);
+      if (permittedVotingRange != null) {
+        if (permittedVotingRange.getMin() > config.getVotingMinScore()) {
+          log.debug("Minimum ChatGPT voting score set to {}", permittedVotingRange.getMin());
+          changeSetData.setVotingMinScore(permittedVotingRange.getMin());
         }
+        if (permittedVotingRange.getMax() < config.getVotingMaxScore()) {
+          log.debug("Maximum ChatGPT voting score set to {}", permittedVotingRange.getMax());
+          changeSetData.setVotingMaxScore(permittedVotingRange.getMax());
+        }
+      }
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataProvider.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataProvider.java
index 616aec4..f0ac342 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/ChangeSetDataProvider.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.data;
 
 import com.google.gerrit.server.account.AccountCache;
@@ -7,17 +21,18 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 
 public class ChangeSetDataProvider implements Provider<ChangeSetData> {
-    private final int gptAccountId;
-    private final Configuration config;
+  private final int gptAccountId;
+  private final Configuration config;
 
-    @Inject
-    ChangeSetDataProvider(Configuration config, AccountCache accountCache) {
-        this.config = config;
-        this.gptAccountId = accountCache.getByUsername(config.getGerritUserName()).get().account().id().get();
-    }
+  @Inject
+  ChangeSetDataProvider(Configuration config, AccountCache accountCache) {
+    this.config = config;
+    this.gptAccountId =
+        accountCache.getByUsername(config.getGerritUserName()).get().account().id().get();
+  }
 
-    @Override
-    public ChangeSetData get() {
-        return new ChangeSetData(gptAccountId, config.getVotingMinScore(), config.getVotingMaxScore());
-    }
+  @Override
+  public ChangeSetData get() {
+    return new ChangeSetData(gptAccountId, config.getVotingMinScore(), config.getVotingMaxScore());
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandler.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandler.java
index 59e6474..798d79e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandler.java
@@ -1,9 +1,24 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.data;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.nio.file.Files;
@@ -11,74 +26,71 @@
 import java.util.Map;
 import java.util.Properties;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-
 @Singleton
 public class PluginDataHandler {
-    private final Path configFile;
-    private final Properties configProperties = new Properties();
+  private final Path configFile;
+  private final Properties configProperties = new Properties();
 
-    @Inject
-    public PluginDataHandler(Path configFilePath) {
-        this.configFile = configFilePath;
-        try {
-            loadOrCreateProperties();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+  @Inject
+  public PluginDataHandler(Path configFilePath) {
+    this.configFile = configFilePath;
+    try {
+      loadOrCreateProperties();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
     }
+  }
 
-    public synchronized void setValue(String key, String value) {
-        configProperties.setProperty(key, value);
-        storeProperties();
-    }
+  public synchronized void setValue(String key, String value) {
+    configProperties.setProperty(key, value);
+    storeProperties();
+  }
 
-    public synchronized void setJsonValue(String key, Object value) {
-        setValue(key, getGson().toJson(value));
-    }
+  public synchronized void setJsonValue(String key, Object value) {
+    setValue(key, getGson().toJson(value));
+  }
 
-    public String getValue(String key) {
-        return configProperties.getProperty(key);
-    }
+  public String getValue(String key) {
+    return configProperties.getProperty(key);
+  }
 
-    public <T> Map<String, T> getJsonValue(String key, Class<T> clazz) {
-        String value = getValue(key);
-        if (value == null || value.isEmpty()) return null;
-        Type typeOfMap = TypeToken.getParameterized(Map.class, String.class, clazz).getType();
-        return getGson().fromJson(value, typeOfMap);
-    }
+  public <T> Map<String, T> getJsonValue(String key, Class<T> clazz) {
+    String value = getValue(key);
+    if (value == null || value.isEmpty()) return null;
+    Type typeOfMap = TypeToken.getParameterized(Map.class, String.class, clazz).getType();
+    return getGson().fromJson(value, typeOfMap);
+  }
 
-    public synchronized void removeValue(String key) {
-        if (configProperties.containsKey(key)) {
-            configProperties.remove(key);
-            storeProperties();
-        }
+  public synchronized void removeValue(String key) {
+    if (configProperties.containsKey(key)) {
+      configProperties.remove(key);
+      storeProperties();
     }
+  }
 
-    public synchronized void destroy() {
-        try {
-            Files.deleteIfExists(configFile);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to delete the config file: " + configFile, e);
-        }
+  public synchronized void destroy() {
+    try {
+      Files.deleteIfExists(configFile);
+    } catch (IOException e) {
+      throw new RuntimeException("Failed to delete the config file: " + configFile, e);
     }
+  }
 
-    private void storeProperties() {
-        try (var output = Files.newOutputStream(configFile)) {
-            configProperties.store(output, null);
-        }
-        catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+  private void storeProperties() {
+    try (var output = Files.newOutputStream(configFile)) {
+      configProperties.store(output, null);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
     }
+  }
 
-    private void loadOrCreateProperties() throws IOException {
-        if (Files.notExists(configFile)) {
-            Files.createFile(configFile);
-        } else {
-            try (var input = Files.newInputStream(configFile)) {
-                configProperties.load(input);
-            }
-        }
+  private void loadOrCreateProperties() throws IOException {
+    if (Files.notExists(configFile)) {
+      Files.createFile(configFile);
+    } else {
+      try (var input = Files.newInputStream(configFile)) {
+        configProperties.load(input);
+      }
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerBaseProvider.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerBaseProvider.java
index 7d4ca18..594d189 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerBaseProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerBaseProvider.java
@@ -1,29 +1,43 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.data;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
-
 import java.nio.file.Path;
 
 @Singleton
 public class PluginDataHandlerBaseProvider implements Provider<PluginDataHandler> {
-    private static final String PATH_SUFFIX = ".data";
-    private static final String PATH_GLOBAL = "global";
+  private static final String PATH_SUFFIX = ".data";
+  private static final String PATH_GLOBAL = "global";
 
-    private final Path defaultPluginDataPath;
+  private final Path defaultPluginDataPath;
 
-    @Inject
-    public PluginDataHandlerBaseProvider(@com.google.gerrit.extensions.annotations.PluginData Path defaultPluginDataPath) {
-        this.defaultPluginDataPath = defaultPluginDataPath;
-    }
+  @Inject
+  public PluginDataHandlerBaseProvider(
+      @com.google.gerrit.extensions.annotations.PluginData Path defaultPluginDataPath) {
+    this.defaultPluginDataPath = defaultPluginDataPath;
+  }
 
-    public PluginDataHandler get(String path) {
-        return new PluginDataHandler(defaultPluginDataPath.resolve(path + PATH_SUFFIX));
-    }
+  public PluginDataHandler get(String path) {
+    return new PluginDataHandler(defaultPluginDataPath.resolve(path + PATH_SUFFIX));
+  }
 
-    @Override
-    public PluginDataHandler get() {
-        return get(PATH_GLOBAL);
-    }
+  @Override
+  public PluginDataHandler get() {
+    return get(PATH_GLOBAL);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerProvider.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerProvider.java
index 7c7d855..0ea85f9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/data/PluginDataHandlerProvider.java
@@ -1,46 +1,59 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.data;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.sanitizeFilename;
+
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
-
 import java.nio.file.Path;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.sanitizeFilename;
-
 @Singleton
-public class PluginDataHandlerProvider extends PluginDataHandlerBaseProvider implements Provider<PluginDataHandler> {
-    private static final String PATH_ASSISTANTS = ".assistants";
+public class PluginDataHandlerProvider extends PluginDataHandlerBaseProvider
+    implements Provider<PluginDataHandler> {
+  private static final String PATH_ASSISTANTS = ".assistants";
 
-    private final String projectName;
-    private final String changeKey;
-    private final String assistantsWorkspace;
+  private final String projectName;
+  private final String changeKey;
+  private final String assistantsWorkspace;
 
-    @Inject
-    public PluginDataHandlerProvider(
-            @com.google.gerrit.extensions.annotations.PluginData Path defaultPluginDataPath,
-            GerritChange change
-    ) {
-        super(defaultPluginDataPath);
-        projectName = sanitizeFilename(change.getProjectName());
-        changeKey = change.getChangeKey().toString();
-        assistantsWorkspace = projectName + PATH_ASSISTANTS;
-    }
+  @Inject
+  public PluginDataHandlerProvider(
+      @com.google.gerrit.extensions.annotations.PluginData Path defaultPluginDataPath,
+      GerritChange change) {
+    super(defaultPluginDataPath);
+    projectName = sanitizeFilename(change.getProjectName());
+    changeKey = change.getChangeKey().toString();
+    assistantsWorkspace = projectName + PATH_ASSISTANTS;
+  }
 
-    public PluginDataHandler getGlobalScope() {
-        return super.get();
-    }
+  public PluginDataHandler getGlobalScope() {
+    return super.get();
+  }
 
-    public PluginDataHandler getProjectScope() {
-        return super.get(projectName);
-    }
+  public PluginDataHandler getProjectScope() {
+    return super.get(projectName);
+  }
 
-    public PluginDataHandler getChangeScope() {
-        return super.get(changeKey);
-    }
+  public PluginDataHandler getChangeScope() {
+    return super.get(changeKey);
+  }
 
-    public PluginDataHandler getAssistantsWorkspace() {
-        return super.get(assistantsWorkspace);
-    }
+  public PluginDataHandler getAssistantsWorkspace() {
+    return super.get(assistantsWorkspace);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/listener/IEventHandlerType.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/listener/IEventHandlerType.java
index dac591c..42cb319 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/listener/IEventHandlerType.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/listener/IEventHandlerType.java
@@ -1,10 +1,27 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.interfaces.listener;
 
 public interface IEventHandlerType {
-    enum PreprocessResult {
-        OK, EXIT, SWITCH_TO_PATCH_SET_CREATED
-    }
+  enum PreprocessResult {
+    OK,
+    EXIT,
+    SWITCH_TO_PATCH_SET_CREATED
+  }
 
-    PreprocessResult preprocessEvent();
-    void processEvent() throws Exception;
+  PreprocessResult preprocessEvent();
+
+  void processEvent() throws Exception;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/gerrit/GerritClientPatchSet.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/gerrit/GerritClientPatchSet.java
index d265c97..8044bd2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/gerrit/GerritClientPatchSet.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/gerrit/GerritClientPatchSet.java
@@ -1,17 +1,36 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.gerrit;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-
 import java.util.HashMap;
 
 public interface GerritClientPatchSet {
-    String getPatchSet(ChangeSetData changeSetData, GerritChange gerritChange) throws Exception;
-    boolean isDisabledUser(String authorUsername);
-    boolean isDisabledTopic(String topic);
-    void retrieveRevisionBase(GerritChange change);
-    Integer getNotNullAccountId(String authorUsername);
-    HashMap<String, FileDiffProcessed> getFileDiffsProcessed();
-    Integer getRevisionBase();
+  String getPatchSet(ChangeSetData changeSetData, GerritChange gerritChange) throws Exception;
+
+  boolean isDisabledUser(String authorUsername);
+
+  boolean isDisabledTopic(String topic);
+
+  void retrieveRevisionBase(GerritChange change);
+
+  Integer getNotNullAccountId(String authorUsername);
+
+  HashMap<String, FileDiffProcessed> getFileDiffsProcessed();
+
+  Integer getRevisionBase();
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/openapi/ChatAIClient.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/openapi/ChatAIClient.java
index e70b2d3..244b3ef 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/openapi/ChatAIClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/api/openapi/ChatAIClient.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.openapi;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
@@ -5,7 +19,8 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 
 public interface ChatAIClient {
-    AIChatResponseContent ask(ChangeSetData changeSetData, GerritChange change, String patchSet)
-            throws Exception;
-    String getRequestBody();
+  AIChatResponseContent ask(ChangeSetData changeSetData, GerritChange change, String patchSet)
+      throws Exception;
+
+  String getRequestBody();
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/prompt/ChatAIDataPrompt.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/prompt/ChatAIDataPrompt.java
index 0869ad9..bd055b1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/prompt/ChatAIDataPrompt.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/common/client/prompt/ChatAIDataPrompt.java
@@ -1,12 +1,27 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.prompt;
 
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatMessageItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
-
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatMessageItem;
 import java.util.List;
 
 public interface ChatAIDataPrompt {
-    void addMessageItem(int i);
-    List<GerritComment> getCommentProperties();
-    List<AIChatMessageItem> getMessageItems();
+  void addMessageItem(int i);
+
+  List<GerritComment> getCommentProperties();
+
+  List<AIChatMessageItem> getMessageItems();
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/stateful/client/prompt/ChatGptPromptStateful.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/stateful/client/prompt/ChatGptPromptStateful.java
index 087a9e9..9d340c6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/stateful/client/prompt/ChatGptPromptStateful.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/interfaces/mode/stateful/client/prompt/ChatGptPromptStateful.java
@@ -1,12 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt;
 
 import java.util.List;
 
 public interface ChatGptPromptStateful {
-    void addGptAssistantInstructions(List<String> instructions);
-    String getDefaultGptAssistantDescription();
-    String getDefaultGptAssistantInstructions();
-    String getDefaultGptThreadReviewMessage(String patchSet);
-    String getAIRequestDataPrompt();
-    void setCommentEvent(boolean isCommentEvent);
+  void addGptAssistantInstructions(List<String> instructions);
+
+  String getDefaultGptAssistantDescription();
+
+  String getDefaultGptAssistantInstructions();
+
+  String getDefaultGptThreadReviewMessage(String patchSet);
+
+  String getAIRequestDataPrompt();
+
+  void setCommentEvent(boolean isCommentEvent);
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerExecutor.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerExecutor.java
index 2d0cf62..8efe7ed 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerExecutor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerExecutor.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
 import com.google.gerrit.extensions.annotations.PluginName;
@@ -5,36 +19,34 @@
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.inject.Inject;
-import com.google.inject.Singleton;
 import com.google.inject.Injector;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.concurrent.ScheduledExecutorService;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 @Singleton
 public class EventHandlerExecutor {
-    private final Injector injector;
-    private final ScheduledExecutorService executor;
+  private final Injector injector;
+  private final ScheduledExecutorService executor;
 
-    @Inject
-    EventHandlerExecutor(
-            Injector injector,
-            WorkQueue workQueue,
-            @PluginName String pluginName,
-            PluginConfigFactory pluginConfigFactory
-    ) {
-        this.injector = injector;
-        int maximumPoolSize = pluginConfigFactory.getFromGerritConfig(pluginName)
-                .getInt("maximumPoolSize", 2);
-        this.executor = workQueue.createQueue(maximumPoolSize, "ChatGPT request executor");
-    }
+  @Inject
+  EventHandlerExecutor(
+      Injector injector,
+      WorkQueue workQueue,
+      @PluginName String pluginName,
+      PluginConfigFactory pluginConfigFactory) {
+    this.injector = injector;
+    int maximumPoolSize =
+        pluginConfigFactory.getFromGerritConfig(pluginName).getInt("maximumPoolSize", 2);
+    this.executor = workQueue.createQueue(maximumPoolSize, "ChatGPT request executor");
+  }
 
-    public void execute(Configuration config, Event event) {
-        GerritEventContextModule contextModule = new GerritEventContextModule(config, event);
-        EventHandlerTask task = injector.createChildInjector(contextModule)
-                .getInstance(EventHandlerTask.class);
-        executor.execute(task);
-    }
+  public void execute(Configuration config, Event event) {
+    GerritEventContextModule contextModule = new GerritEventContextModule(config, event);
+    EventHandlerTask task =
+        injector.createChildInjector(contextModule).getInstance(EventHandlerTask.class);
+    executor.execute(task);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTask.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTask.java
index 2e5931e..0c28cf2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTask.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTask.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -15,154 +29,159 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.git.GitRepoFiles;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class EventHandlerTask implements Runnable {
-    @VisibleForTesting
-    public enum Result {
-        OK, NOT_SUPPORTED, FAILURE
-    }
-    public enum SupportedEvents {
-        PATCH_SET_CREATED,
-        COMMENT_ADDED,
-        CHANGE_MERGED
+  @VisibleForTesting
+  public enum Result {
+    OK,
+    NOT_SUPPORTED,
+    FAILURE
+  }
+
+  public enum SupportedEvents {
+    PATCH_SET_CREATED,
+    COMMENT_ADDED,
+    CHANGE_MERGED
+  }
+
+  public static final Map<SupportedEvents, Class<?>> EVENT_CLASS_MAP =
+      Map.of(
+          SupportedEvents.PATCH_SET_CREATED, PatchSetCreatedEvent.class,
+          SupportedEvents.COMMENT_ADDED, CommentAddedEvent.class,
+          SupportedEvents.CHANGE_MERGED, ChangeMergedEvent.class);
+
+  private static final Map<String, SupportedEvents> EVENT_TYPE_MAP =
+      Map.of(
+          "patchset-created", SupportedEvents.PATCH_SET_CREATED,
+          "comment-added", SupportedEvents.COMMENT_ADDED,
+          "change-merged", SupportedEvents.CHANGE_MERGED);
+
+  private final Configuration config;
+  private final GerritClient gerritClient;
+  private final ChangeSetData changeSetData;
+  private final GerritChange change;
+  private final PatchSetReviewer reviewer;
+  private final GitRepoFiles gitRepoFiles;
+  private final PluginDataHandlerProvider pluginDataHandlerProvider;
+
+  private SupportedEvents processing_event_type;
+  private IEventHandlerType eventHandlerType;
+
+  @Inject
+  EventHandlerTask(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      PatchSetReviewer reviewer,
+      GerritClient gerritClient,
+      GitRepoFiles gitRepoFiles,
+      PluginDataHandlerProvider pluginDataHandlerProvider) {
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.reviewer = reviewer;
+    this.gerritClient = gerritClient;
+    this.config = config;
+    this.gitRepoFiles = gitRepoFiles;
+    this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  }
+
+  @Override
+  public void run() {
+    execute();
+  }
+
+  @VisibleForTesting
+  public Result execute() {
+    if (!preProcessEvent()) {
+      return Result.NOT_SUPPORTED;
     }
 
-    public static final Map<SupportedEvents, Class<?>> EVENT_CLASS_MAP = Map.of(
-            SupportedEvents.PATCH_SET_CREATED, PatchSetCreatedEvent.class,
-            SupportedEvents.COMMENT_ADDED, CommentAddedEvent.class,
-            SupportedEvents.CHANGE_MERGED, ChangeMergedEvent.class
-    );
+    try {
+      log.info("Processing change: {}", change.getFullChangeId());
+      eventHandlerType.processEvent();
+      log.info("Finished processing change: {}", change.getFullChangeId());
+    } catch (Exception e) {
+      log.error("Error while processing change: {}", change.getFullChangeId(), e);
+      if (e instanceof InterruptedException) {
+        Thread.currentThread().interrupt();
+      }
+      return Result.FAILURE;
+    }
+    return Result.OK;
+  }
 
-    private static final Map<String, SupportedEvents> EVENT_TYPE_MAP = Map.of(
-        "patchset-created", SupportedEvents.PATCH_SET_CREATED,
-        "comment-added", SupportedEvents.COMMENT_ADDED,
-        "change-merged", SupportedEvents.CHANGE_MERGED
-    );
-
-    private final Configuration config;
-    private final GerritClient gerritClient;
-    private final ChangeSetData changeSetData;
-    private final GerritChange change;
-    private final PatchSetReviewer reviewer;
-    private final GitRepoFiles gitRepoFiles;
-    private final PluginDataHandlerProvider pluginDataHandlerProvider;
-
-    private SupportedEvents processing_event_type;
-    private IEventHandlerType eventHandlerType;
-
-    @Inject
-    EventHandlerTask(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            PatchSetReviewer reviewer,
-            GerritClient gerritClient,
-            GitRepoFiles gitRepoFiles,
-            PluginDataHandlerProvider pluginDataHandlerProvider
-    ) {
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.reviewer = reviewer;
-        this.gerritClient = gerritClient;
-        this.config = config;
-        this.gitRepoFiles = gitRepoFiles;
-        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  private boolean preProcessEvent() {
+    String eventType = Optional.ofNullable(change.getEventType()).orElse("");
+    log.info("Event type {}", eventType);
+    processing_event_type = EVENT_TYPE_MAP.get(eventType);
+    if (processing_event_type == null) {
+      return false;
     }
 
-    @Override
-    public void run() {
-        execute();
+    if (!isReviewEnabled(change)) {
+      return false;
     }
 
-    @VisibleForTesting
-    public Result execute() {
-        if (!preProcessEvent()) {
-            return Result.NOT_SUPPORTED;
+    while (true) {
+      eventHandlerType = getEventHandlerType();
+      switch (eventHandlerType.preprocessEvent()) {
+        case EXIT -> {
+          return false;
         }
-
-        try {
-            log.info("Processing change: {}", change.getFullChangeId());
-            eventHandlerType.processEvent();
-            log.info("Finished processing change: {}", change.getFullChangeId());
-        } catch (Exception e) {
-            log.error("Error while processing change: {}", change.getFullChangeId(), e);
-            if (e instanceof InterruptedException) {
-                Thread.currentThread().interrupt();
-            }
-            return Result.FAILURE;
+        case SWITCH_TO_PATCH_SET_CREATED -> {
+          processing_event_type = SupportedEvents.PATCH_SET_CREATED;
+          continue;
         }
-        return Result.OK;
+      }
+      break;
     }
 
-    private boolean preProcessEvent() {
-        String eventType = Optional.ofNullable(change.getEventType()).orElse("");
-        log.info("Event type {}", eventType);
-        processing_event_type = EVENT_TYPE_MAP.get(eventType);
-        if (processing_event_type == null) {
-            return false;
-        }
+    return true;
+  }
 
-        if (!isReviewEnabled(change)) {
-            return false;
-        }
+  private IEventHandlerType getEventHandlerType() {
+    return switch (processing_event_type) {
+      case PATCH_SET_CREATED ->
+          new EventHandlerTypePatchSetReview(config, changeSetData, change, reviewer, gerritClient);
+      case COMMENT_ADDED ->
+          new EventHandlerTypeCommentAdded(changeSetData, change, reviewer, gerritClient);
+      case CHANGE_MERGED ->
+          new EventHandlerTypeChangeMerged(
+              config, changeSetData, change, gitRepoFiles, pluginDataHandlerProvider);
+    };
+  }
 
-        while (true) {
-            eventHandlerType = getEventHandlerType();
-            switch (eventHandlerType.preprocessEvent()) {
-                case EXIT -> {
-                    return false;
-                }
-                case SWITCH_TO_PATCH_SET_CREATED -> {
-                    processing_event_type = SupportedEvents.PATCH_SET_CREATED;
-                    continue;
-                }
-            }
-            break;
-        }
-
-        return true;
+  private boolean isReviewEnabled(GerritChange change) {
+    List<String> enabledProjects =
+        Splitter.on(",").omitEmptyStrings().splitToList(config.getEnabledProjects());
+    if (!config.isGlobalEnable()
+        && !enabledProjects.contains(change.getProjectNameKey().get())
+        && !config.isProjectEnable()) {
+      log.debug("The project {} is not enabled for review", change.getProjectNameKey());
+      return false;
     }
 
-    private IEventHandlerType getEventHandlerType() {
-        return switch (processing_event_type) {
-            case PATCH_SET_CREATED -> new EventHandlerTypePatchSetReview(config, changeSetData, change, reviewer, gerritClient);
-            case COMMENT_ADDED -> new EventHandlerTypeCommentAdded(changeSetData, change, reviewer, gerritClient);
-            case CHANGE_MERGED -> new EventHandlerTypeChangeMerged(config, changeSetData, change, gitRepoFiles, pluginDataHandlerProvider);
-        };
+    String topic = getTopic(change).orElse("");
+    log.debug("PatchSet Topic retrieved: '{}'", topic);
+    if (gerritClient.isDisabledTopic(topic)) {
+      log.info("Disabled review for PatchSets with Topic '{}'", topic);
+      return false;
     }
 
-    private boolean isReviewEnabled(GerritChange change) {
-        List<String> enabledProjects = Splitter.on(",").omitEmptyStrings()
-                .splitToList(config.getEnabledProjects());
-        if (!config.isGlobalEnable() &&
-                !enabledProjects.contains(change.getProjectNameKey().get()) &&
-                !config.isProjectEnable()) {
-            log.debug("The project {} is not enabled for review", change.getProjectNameKey());
-            return false;
-        }
+    return true;
+  }
 
-        String topic = getTopic(change).orElse("");
-        log.debug("PatchSet Topic retrieved: '{}'", topic);
-        if (gerritClient.isDisabledTopic(topic)) {
-            log.info("Disabled review for PatchSets with Topic '{}'", topic);
-            return false;
-        }
-
-        return true;
+  private Optional<String> getTopic(GerritChange change) {
+    try {
+      ChangeAttribute changeAttribute = change.getPatchSetEvent().change.get();
+      return Optional.ofNullable(changeAttribute.topic);
+    } catch (NullPointerException e) {
+      return Optional.empty();
     }
-
-    private Optional<String> getTopic(GerritChange change) {
-        try {
-            ChangeAttribute changeAttribute = change.getPatchSetEvent().change.get();
-            return Optional.ofNullable(changeAttribute.topic);
-        } catch (NullPointerException e) {
-            return Optional.empty();
-        }
-    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeChangeMerged.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeChangeMerged.java
index 1e4af18..bf70a6b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeChangeMerged.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeChangeMerged.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -11,43 +25,39 @@
 
 @Slf4j
 public class EventHandlerTypeChangeMerged implements IEventHandlerType {
-    private final Configuration config;
-    private final ChangeSetData changeSetData;
-    private final GerritChange change;
-    private final GitRepoFiles gitRepoFiles;
-    private final PluginDataHandlerProvider pluginDataHandlerProvider;
+  private final Configuration config;
+  private final ChangeSetData changeSetData;
+  private final GerritChange change;
+  private final GitRepoFiles gitRepoFiles;
+  private final PluginDataHandlerProvider pluginDataHandlerProvider;
 
-    EventHandlerTypeChangeMerged(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            GitRepoFiles gitRepoFiles,
-            PluginDataHandlerProvider pluginDataHandlerProvider
-    ) {
-        this.config = config;
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.gitRepoFiles = gitRepoFiles;
-        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
-    }
+  EventHandlerTypeChangeMerged(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      GitRepoFiles gitRepoFiles,
+      PluginDataHandlerProvider pluginDataHandlerProvider) {
+    this.config = config;
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.gitRepoFiles = gitRepoFiles;
+    this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  }
 
-    @Override
-    public PreprocessResult preprocessEvent() {
-        return PreprocessResult.OK;
-    }
+  @Override
+  public PreprocessResult preprocessEvent() {
+    return PreprocessResult.OK;
+  }
 
-    @Override
-    public void processEvent() {
-        // TODO: Should we be firing assistant based stateful request items when the aiMode is stateless?
-        // This is extra paid for requests which aren't required if using GPT.
-        ChatGptAssistant chatGptAssistant = new ChatGptAssistant(
-                config,
-                changeSetData,
-                change,
-                gitRepoFiles,
-                pluginDataHandlerProvider
-        );
-        chatGptAssistant.flushAssistantIds();
-        chatGptAssistant.createVectorStore();
-    }
+  @Override
+  public void processEvent() {
+    // TODO: Should we be firing assistant based stateful request items when the aiMode is
+    // stateless?
+    // This is extra paid for requests which aren't required if using GPT.
+    ChatGptAssistant chatGptAssistant =
+        new ChatGptAssistant(
+            config, changeSetData, change, gitRepoFiles, pluginDataHandlerProvider);
+    chatGptAssistant.flushAssistantIds();
+    chatGptAssistant.createVectorStore();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeCommentAdded.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeCommentAdded.java
index 6d25df4..439b9a0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeCommentAdded.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypeCommentAdded.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
 import com.googlesource.gerrit.plugins.aicodereview.PatchSetReviewer;
@@ -9,41 +23,39 @@
 
 @Slf4j
 public class EventHandlerTypeCommentAdded implements IEventHandlerType {
-    private final ChangeSetData changeSetData;
-    private final GerritChange change;
-    private final PatchSetReviewer reviewer;
-    private final GerritClient gerritClient;
+  private final ChangeSetData changeSetData;
+  private final GerritChange change;
+  private final PatchSetReviewer reviewer;
+  private final GerritClient gerritClient;
 
-    EventHandlerTypeCommentAdded(
-            ChangeSetData changeSetData,
-            GerritChange change,
-            PatchSetReviewer reviewer,
-            GerritClient gerritClient
-    ) {
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.reviewer = reviewer;
-        this.gerritClient = gerritClient;
+  EventHandlerTypeCommentAdded(
+      ChangeSetData changeSetData,
+      GerritChange change,
+      PatchSetReviewer reviewer,
+      GerritClient gerritClient) {
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.reviewer = reviewer;
+    this.gerritClient = gerritClient;
+  }
 
+  @Override
+  public PreprocessResult preprocessEvent() {
+    if (!gerritClient.retrieveLastComments(change)) {
+      if (changeSetData.getForcedReview()) {
+        return PreprocessResult.SWITCH_TO_PATCH_SET_CREATED;
+      } else {
+        log.info("No comments found for review");
+        return PreprocessResult.EXIT;
+      }
     }
+    change.setIsCommentEvent(true);
 
-    @Override
-    public PreprocessResult preprocessEvent() {
-        if (!gerritClient.retrieveLastComments(change)) {
-            if (changeSetData.getForcedReview()) {
-                return PreprocessResult.SWITCH_TO_PATCH_SET_CREATED;
-            } else {
-                log.info("No comments found for review");
-                return PreprocessResult.EXIT;
-            }
-        }
-        change.setIsCommentEvent(true);
+    return PreprocessResult.OK;
+  }
 
-        return PreprocessResult.OK;
-    }
-
-    @Override
-    public void processEvent() throws Exception {
-        reviewer.review(change);
-    }
+  @Override
+  public void processEvent() throws Exception {
+    reviewer.review(change);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypePatchSetReview.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypePatchSetReview.java
index d1ecbd4..9c8f2f9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypePatchSetReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/EventHandlerTypePatchSetReview.java
@@ -1,5 +1,21 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
+import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
+
 import com.google.gerrit.extensions.client.ChangeKind;
 import com.google.gerrit.server.data.PatchSetAttribute;
 import com.googlesource.gerrit.plugins.aicodereview.PatchSetReviewer;
@@ -8,77 +24,74 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.Optional;
-
-import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class EventHandlerTypePatchSetReview implements IEventHandlerType {
-    private final Configuration config;
-    private final ChangeSetData changeSetData;
-    private final GerritChange change;
-    private final PatchSetReviewer reviewer;
-    private final GerritClient gerritClient;
+  private final Configuration config;
+  private final ChangeSetData changeSetData;
+  private final GerritChange change;
+  private final PatchSetReviewer reviewer;
+  private final GerritClient gerritClient;
 
-    EventHandlerTypePatchSetReview(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            PatchSetReviewer reviewer,
-            GerritClient gerritClient
-    ) {
-        this.config = config;
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.reviewer = reviewer;
-        this.gerritClient = gerritClient;
+  EventHandlerTypePatchSetReview(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      PatchSetReviewer reviewer,
+      GerritClient gerritClient) {
+    this.config = config;
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.reviewer = reviewer;
+    this.gerritClient = gerritClient;
+  }
+
+  @Override
+  public PreprocessResult preprocessEvent() {
+    if (!isPatchSetReviewEnabled(change)) {
+      log.debug("Patch Set review disabled");
+      return PreprocessResult.EXIT;
     }
+    gerritClient.retrievePatchSetInfo(change);
 
-    @Override
-    public PreprocessResult preprocessEvent() {
-        if (!isPatchSetReviewEnabled(change)) {
-            log.debug("Patch Set review disabled");
-            return PreprocessResult.EXIT;
-        }
-        gerritClient.retrievePatchSetInfo(change);
+    return PreprocessResult.OK;
+  }
 
-        return PreprocessResult.OK;
+  @Override
+  public void processEvent() throws Exception {
+    reviewer.review(change);
+  }
+
+  private boolean isPatchSetReviewEnabled(GerritChange change) {
+    if (!config.getAIReviewPatchSet()) {
+      log.debug("Disabled review function for created or updated PatchSets.");
+      return false;
     }
-
-    @Override
-    public void processEvent() throws Exception {
-        reviewer.review(change);
+    Optional<PatchSetAttribute> patchSetAttributeOptional = change.getPatchSetAttribute();
+    if (patchSetAttributeOptional.isEmpty()) {
+      log.info("PatchSetAttribute event properties not retrieved");
+      return false;
     }
-
-    private boolean isPatchSetReviewEnabled(GerritChange change) {
-        if (!config.getAIReviewPatchSet()) {
-            log.debug("Disabled review function for created or updated PatchSets.");
-            return false;
-        }
-        Optional<PatchSetAttribute> patchSetAttributeOptional = change.getPatchSetAttribute();
-        if (patchSetAttributeOptional.isEmpty()) {
-            log.info("PatchSetAttribute event properties not retrieved");
-            return false;
-        }
-        PatchSetAttribute patchSetAttribute = patchSetAttributeOptional.get();
-        ChangeKind patchSetEventKind = patchSetAttribute.kind;
-        // The only Change kind that automatically triggers the review is REWORK. If review is forced via command, this
-        // condition is bypassed
-        if (patchSetEventKind != REWORK && !changeSetData.getForcedReview()) {
-            log.debug("Change kind '{}' not processed", patchSetEventKind);
-            return false;
-        }
-        String authorUsername = patchSetAttribute.author.username;
-        if (gerritClient.isDisabledUser(authorUsername)) {
-            log.info("Review of PatchSets from user '{}' is disabled.", authorUsername);
-            return false;
-        }
-        if (gerritClient.isWorkInProgress(change)) {
-            log.debug("Skipping Patch Set processing due to its WIP status.");
-            return false;
-        }
-        return true;
+    PatchSetAttribute patchSetAttribute = patchSetAttributeOptional.get();
+    ChangeKind patchSetEventKind = patchSetAttribute.kind;
+    // The only Change kind that automatically triggers the review is REWORK. If review is forced
+    // via command, this
+    // condition is bypassed
+    if (patchSetEventKind != REWORK && !changeSetData.getForcedReview()) {
+      log.debug("Change kind '{}' not processed", patchSetEventKind);
+      return false;
     }
+    String authorUsername = patchSetAttribute.author.username;
+    if (gerritClient.isDisabledUser(authorUsername)) {
+      log.info("Review of PatchSets from user '{}' is disabled.", authorUsername);
+      return false;
+    }
+    if (gerritClient.isWorkInProgress(change)) {
+      log.debug("Skipping Patch Set processing due to its WIP status.");
+      return false;
+    }
+    return true;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritEventContextModule.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritEventContextModule.java
index 423a09f..9eebf35 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritEventContextModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritEventContextModule.java
@@ -1,5 +1,21 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
+import static com.google.inject.Scopes.SINGLETON;
+
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.server.events.Event;
 import com.google.inject.Singleton;
@@ -8,47 +24,45 @@
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.openapi.ChatAIClient;
-import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.gerrit.GerritClientPatchSet;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientPatchSet;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatai.AIChatClientStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit.GerritClientPatchSetStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.chatai.AIChatClientStateless;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.gerrit.GerritClientPatchSetStateless;
 
-import static com.google.inject.Scopes.SINGLETON;
-
 public class GerritEventContextModule extends FactoryModule {
-    private final Event event;
-    private final Configuration config;
+  private final Event event;
+  private final Configuration config;
 
-    public GerritEventContextModule(Configuration config, Event event) {
-        this.event = event;
-        this.config = config;
-    }
+  public GerritEventContextModule(Configuration config, Event event) {
+    this.event = event;
+    this.config = config;
+  }
 
-    @Override
-    protected void configure() {
-        bind(ChatAIClient.class).to(getChatAIMode());
-        bind(GerritClientPatchSet.class).to(getClientPatchSet());
+  @Override
+  protected void configure() {
+    bind(ChatAIClient.class).to(getChatAIMode());
+    bind(GerritClientPatchSet.class).to(getClientPatchSet());
 
-        bind(Configuration.class).toInstance(config);
-        bind(GerritChange.class).toInstance(new GerritChange(event));
-        bind(ChangeSetData.class).toProvider(ChangeSetDataProvider.class).in(SINGLETON);
-        bind(PluginDataHandler.class).toProvider(PluginDataHandlerProvider.class).in(Singleton.class);
-    }
+    bind(Configuration.class).toInstance(config);
+    bind(GerritChange.class).toInstance(new GerritChange(event));
+    bind(ChangeSetData.class).toProvider(ChangeSetDataProvider.class).in(SINGLETON);
+    bind(PluginDataHandler.class).toProvider(PluginDataHandlerProvider.class).in(Singleton.class);
+  }
 
-    private Class<? extends ChatAIClient> getChatAIMode() {
-        return switch (config.getAIMode()){
-            case stateful -> AIChatClientStateful.class;
-            case stateless -> AIChatClientStateless.class;
-        };
-    }
+  private Class<? extends ChatAIClient> getChatAIMode() {
+    return switch (config.getAIMode()) {
+      case stateful -> AIChatClientStateful.class;
+      case stateless -> AIChatClientStateless.class;
+    };
+  }
 
-    private Class<? extends GerritClientPatchSet> getClientPatchSet() {
-        return switch (config.getAIMode()){
-            case stateful -> GerritClientPatchSetStateful.class;
-            case stateless -> GerritClientPatchSetStateless.class;
-        };
-    }
+  private Class<? extends GerritClientPatchSet> getClientPatchSet() {
+    return switch (config.getAIMode()) {
+      case stateful -> GerritClientPatchSetStateful.class;
+      case stateless -> GerritClientPatchSetStateless.class;
+    };
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritListener.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritListener.java
index 0bf3e21..5e03787 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritListener.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/listener/GerritListener.java
@@ -1,58 +1,72 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.listener;
 
+import static com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.EVENT_CLASS_MAP;
+
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.config.GerritInstanceId;
-import com.google.gerrit.server.events.*;
+import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventListener;
+import com.google.gerrit.server.events.PatchSetEvent;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.aicodereview.config.ConfigCreator;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.Objects;
-
-import static com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.EVENT_CLASS_MAP;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class GerritListener implements EventListener {
-    private final String myInstanceId;
-    private final ConfigCreator configCreator;
-    private final EventHandlerExecutor evenHandlerExecutor;
+  private final String myInstanceId;
+  private final ConfigCreator configCreator;
+  private final EventHandlerExecutor evenHandlerExecutor;
 
-    @Inject
-    public GerritListener(
-            ConfigCreator configCreator,
-            EventHandlerExecutor evenHandlerExecutor,
-            @GerritInstanceId @Nullable String myInstanceId
-    ) {
-        this.configCreator = configCreator;
-        this.evenHandlerExecutor = evenHandlerExecutor;
-        this.myInstanceId = myInstanceId;
+  @Inject
+  public GerritListener(
+      ConfigCreator configCreator,
+      EventHandlerExecutor evenHandlerExecutor,
+      @GerritInstanceId @Nullable String myInstanceId) {
+    this.configCreator = configCreator;
+    this.evenHandlerExecutor = evenHandlerExecutor;
+    this.myInstanceId = myInstanceId;
+  }
+
+  @Override
+  public void onEvent(Event event) {
+    if (!Objects.equals(event.instanceId, myInstanceId)) {
+      log.debug("Ignore event from another instance");
+      return;
+    }
+    if (!EVENT_CLASS_MAP.containsValue(event.getClass())) {
+      log.debug("The event {} is not managed by the plugin", event);
+      return;
     }
 
-    @Override
-    public void onEvent(Event event) {
-        if (!Objects.equals(event.instanceId, myInstanceId)) {
-            log.debug("Ignore event from another instance");
-            return;
-        }
-        if (!EVENT_CLASS_MAP.containsValue(event.getClass())) {
-            log.debug("The event {} is not managed by the plugin", event);
-            return;
-        }
+    log.info("Processing event: {}", event);
+    PatchSetEvent patchSetEvent = (PatchSetEvent) event;
+    Project.NameKey projectNameKey = patchSetEvent.getProjectNameKey();
+    Change.Key changeKey = patchSetEvent.getChangeKey();
 
-        log.info("Processing event: {}", event);
-        PatchSetEvent patchSetEvent = (PatchSetEvent) event;
-        Project.NameKey projectNameKey = patchSetEvent.getProjectNameKey();
-        Change.Key changeKey = patchSetEvent.getChangeKey();
-
-        try {
-            Configuration config = configCreator.createConfig(projectNameKey, changeKey);
-            evenHandlerExecutor.execute(config, patchSetEvent);
-        } catch (NoSuchProjectException e) {
-            log.error("Project not found: {}", projectNameKey, e);
-        }
+    try {
+      Configuration config = configCreator.createConfig(projectNameKey, changeKey);
+      evenHandlerExecutor.execute(config, patchSetEvent);
+    } catch (NoSuchProjectException e) {
+      log.error("Project not found: {}", projectNameKey, e);
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/localization/Localizer.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/localization/Localizer.java
index 6786836..f599b21 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/localization/Localizer.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/localization/Localizer.java
@@ -1,21 +1,35 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.localization;
 
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ResourceBundle;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class Localizer {
-    private final ResourceBundle resourceBundle;
+  private final ResourceBundle resourceBundle;
 
-    @Inject
-    public Localizer(Configuration config) {
-        this.resourceBundle = ResourceBundle.getBundle("localization.localTexts", config.getLocaleDefault());
-    }
+  @Inject
+  public Localizer(Configuration config) {
+    this.resourceBundle =
+        ResourceBundle.getBundle("localization.localTexts", config.getLocaleDefault());
+  }
 
-    public String getText(String key) {
-        return resourceBundle.getString(key);
-    }
+  public String getText(String key) {
+    return resourceBundle.getString(key);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/ClientBase.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/ClientBase.java
index 479be40..4e6860e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/ClientBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/ClientBase.java
@@ -1,11 +1,25 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 
 public abstract class ClientBase {
-    protected Configuration config;
+  protected Configuration config;
 
-    public ClientBase(Configuration config) {
-        this.config = config;
-    }
+  public ClientBase(Configuration config) {
+    this.config = config;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritChange.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritChange.java
index 803c069..f6355d9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritChange.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritChange.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
 import com.google.gerrit.entities.BranchNameKey;
@@ -6,68 +20,70 @@
 import com.google.gerrit.server.data.PatchSetAttribute;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.PatchSetEvent;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Optional;
-
 @Slf4j
 @Getter
 public class GerritChange {
-    private Event event;
-    private String eventType;
-    private long eventTimeStamp;
-    private PatchSetEvent patchSetEvent;
-    private Project.NameKey projectNameKey;
-    private BranchNameKey branchNameKey;
-    private Change.Key changeKey;
-    private String fullChangeId;
-    // "Boolean" is used instead of "boolean" to have "getIsCommentEvent" instead of "isCommentEvent" as getter method
-    // (due to Lombok's magic naming convention)
-    @Setter
-    private Boolean isCommentEvent = false;
+  private Event event;
+  private String eventType;
+  private long eventTimeStamp;
+  private PatchSetEvent patchSetEvent;
+  private Project.NameKey projectNameKey;
+  private BranchNameKey branchNameKey;
+  private Change.Key changeKey;
+  private String fullChangeId;
+  // "Boolean" is used instead of "boolean" to have "getIsCommentEvent" instead of "isCommentEvent"
+  // as getter method
+  // (due to Lombok's magic naming convention)
+  @Setter private Boolean isCommentEvent = false;
 
-    public GerritChange(Project.NameKey projectNameKey, BranchNameKey branchNameKey, Change.Key changeKey) {
-        this.projectNameKey = projectNameKey;
-        this.branchNameKey = branchNameKey;
-        this.changeKey = changeKey;
-        buildFullChangeId();
-    }
+  public GerritChange(
+      Project.NameKey projectNameKey, BranchNameKey branchNameKey, Change.Key changeKey) {
+    this.projectNameKey = projectNameKey;
+    this.branchNameKey = branchNameKey;
+    this.changeKey = changeKey;
+    buildFullChangeId();
+  }
 
-    public GerritChange(Event event) {
-        this(
-                ((PatchSetEvent) event).getProjectNameKey(),
-                ((PatchSetEvent) event).getBranchNameKey(),
-                ((PatchSetEvent) event).getChangeKey()
-        );
-        this.event = event;
-        eventType = event.getType();
-        eventTimeStamp = event.eventCreatedOn;
-        patchSetEvent = (PatchSetEvent) event;
-    }
+  public GerritChange(Event event) {
+    this(
+        ((PatchSetEvent) event).getProjectNameKey(),
+        ((PatchSetEvent) event).getBranchNameKey(),
+        ((PatchSetEvent) event).getChangeKey());
+    this.event = event;
+    eventType = event.getType();
+    eventTimeStamp = event.eventCreatedOn;
+    patchSetEvent = (PatchSetEvent) event;
+  }
 
-    public GerritChange(String fullChangeId) {
-        this.fullChangeId = fullChangeId;
-    }
+  public GerritChange(String fullChangeId) {
+    this.fullChangeId = fullChangeId;
+  }
 
-    public Optional<PatchSetAttribute> getPatchSetAttribute() {
-        try {
-            return Optional.ofNullable(patchSetEvent.patchSet.get());
-        }
-        catch (NullPointerException e) {
-            return Optional.empty();
-        }
+  public Optional<PatchSetAttribute> getPatchSetAttribute() {
+    try {
+      return Optional.ofNullable(patchSetEvent.patchSet.get());
+    } catch (NullPointerException e) {
+      return Optional.empty();
     }
+  }
 
-    public String getProjectName() {
-        return getProjectNameKey().toString();
-    }
+  public String getProjectName() {
+    return getProjectNameKey().toString();
+  }
 
-    private void buildFullChangeId() {
-        fullChangeId = String.join("~", URLEncoder.encode(projectNameKey.get(), StandardCharsets.UTF_8),
-                branchNameKey.shortName(), changeKey.get());
-    }
+  private void buildFullChangeId() {
+    fullChangeId =
+        String.join(
+            "~",
+            URLEncoder.encode(projectNameKey.get(), StandardCharsets.UTF_8),
+            branchNameKey.shortName(),
+            changeKey.get());
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClient.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClient.java
index b2fda14..dfc75e9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClient.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
 import com.google.inject.Inject;
@@ -5,61 +19,60 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPermittedVotingRange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.HashMap;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 @Singleton
 public class GerritClient {
-    private final GerritClientFacade gerritClientFacade;
+  private final GerritClientFacade gerritClientFacade;
 
-    @Inject
-    public GerritClient(GerritClientFacade gerritClientFacade) {
-        this.gerritClientFacade = gerritClientFacade;
-    }
+  @Inject
+  public GerritClient(GerritClientFacade gerritClientFacade) {
+    this.gerritClientFacade = gerritClientFacade;
+  }
 
-    public GerritPermittedVotingRange getPermittedVotingRange(GerritChange change) {
-        return gerritClientFacade.getPermittedVotingRange(change);
-    }
+  public GerritPermittedVotingRange getPermittedVotingRange(GerritChange change) {
+    return gerritClientFacade.getPermittedVotingRange(change);
+  }
 
-    public String getPatchSet(String fullChangeId) throws Exception {
-        return getPatchSet(new GerritChange(fullChangeId));
-    }
+  public String getPatchSet(String fullChangeId) throws Exception {
+    return getPatchSet(new GerritChange(fullChangeId));
+  }
 
-    public String getPatchSet(GerritChange change) throws Exception {
-        return gerritClientFacade.getPatchSet(change);
-    }
+  public String getPatchSet(GerritChange change) throws Exception {
+    return gerritClientFacade.getPatchSet(change);
+  }
 
-    public boolean isDisabledUser(String authorUsername) {
-        return gerritClientFacade.isDisabledUser(authorUsername);
-    }
+  public boolean isDisabledUser(String authorUsername) {
+    return gerritClientFacade.isDisabledUser(authorUsername);
+  }
 
-    public boolean isDisabledTopic(String topic) {
-        return gerritClientFacade.isDisabledTopic(topic);
-    }
+  public boolean isDisabledTopic(String topic) {
+    return gerritClientFacade.isDisabledTopic(topic);
+  }
 
-    public boolean isWorkInProgress(GerritChange change) {
-        return gerritClientFacade.isWorkInProgress(change);
-    }
+  public boolean isWorkInProgress(GerritChange change) {
+    return gerritClientFacade.isWorkInProgress(change);
+  }
 
-    public HashMap<String, FileDiffProcessed> getFileDiffsProcessed(GerritChange change) {
-        return gerritClientFacade.getFileDiffsProcessed();
-    }
+  public HashMap<String, FileDiffProcessed> getFileDiffsProcessed(GerritChange change) {
+    return gerritClientFacade.getFileDiffsProcessed();
+  }
 
-    public Integer getNotNullAccountId(String authorUsername) {
-        return gerritClientFacade.getNotNullAccountId(authorUsername);
-    }
+  public Integer getNotNullAccountId(String authorUsername) {
+    return gerritClientFacade.getNotNullAccountId(authorUsername);
+  }
 
-    public boolean retrieveLastComments(GerritChange change) {
-        return gerritClientFacade.retrieveLastComments(change);
-    }
+  public boolean retrieveLastComments(GerritChange change) {
+    return gerritClientFacade.retrieveLastComments(change);
+  }
 
-    public void retrievePatchSetInfo(GerritChange change) {
-        gerritClientFacade.retrievePatchSetInfo(change);
-    }
+  public void retrievePatchSetInfo(GerritChange change) {
+    gerritClientFacade.retrievePatchSetInfo(change);
+  }
 
-    public GerritClientData getClientData(GerritChange change) {
-        return gerritClientFacade.getClientData(change);
-    }
+  public GerritClientData getClientData(GerritChange change) {
+    return gerritClientFacade.getClientData(change);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientAccount.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientAccount.java
index 25532c4..8933309 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientAccount.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientAccount.java
@@ -1,87 +1,101 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
+import static java.util.stream.Collectors.toList;
+
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
-import lombok.extern.slf4j.Slf4j;
-
-import static java.util.stream.Collectors.toList;
-
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class GerritClientAccount extends GerritClientBase {
-    private final AccountCache accountCache;
+  private final AccountCache accountCache;
 
-    public GerritClientAccount(Configuration config, AccountCache accountCache) {
-        super(config);
-        this.accountCache = accountCache;
-    }
+  public GerritClientAccount(Configuration config, AccountCache accountCache) {
+    super(config);
+    this.accountCache = accountCache;
+  }
 
-    public boolean isDisabledUser(String authorUsername) {
-        List<String> enabledUsers = config.getEnabledUsers();
-        List<String> disabledUsers = config.getDisabledUsers();
-        return !enabledUsers.contains(Configuration.ENABLED_USERS_ALL)
-                && !enabledUsers.contains(authorUsername)
-                || disabledUsers.contains(authorUsername)
-                || isDisabledUserGroup(authorUsername);
-    }
+  public boolean isDisabledUser(String authorUsername) {
+    List<String> enabledUsers = config.getEnabledUsers();
+    List<String> disabledUsers = config.getDisabledUsers();
+    return !enabledUsers.contains(Configuration.ENABLED_USERS_ALL)
+            && !enabledUsers.contains(authorUsername)
+        || disabledUsers.contains(authorUsername)
+        || isDisabledUserGroup(authorUsername);
+  }
 
-    public boolean isDisabledTopic(String topic) {
-        List<String> enabledTopicFilter = config.getEnabledTopicFilter();
-        List<String> disabledTopicFilter = config.getDisabledTopicFilter();
-        return !enabledTopicFilter.contains(Configuration.ENABLED_TOPICS_ALL)
-                && enabledTopicFilter.stream().noneMatch(topic::contains)
-                || !topic.isEmpty() && disabledTopicFilter.stream().anyMatch(topic::contains);
-    }
+  public boolean isDisabledTopic(String topic) {
+    List<String> enabledTopicFilter = config.getEnabledTopicFilter();
+    List<String> disabledTopicFilter = config.getDisabledTopicFilter();
+    return !enabledTopicFilter.contains(Configuration.ENABLED_TOPICS_ALL)
+            && enabledTopicFilter.stream().noneMatch(topic::contains)
+        || !topic.isEmpty() && disabledTopicFilter.stream().anyMatch(topic::contains);
+  }
 
-    protected Optional<Integer> getAccountId(String authorUsername) {
-        try {
-            return accountCache
-                .getByUsername(authorUsername)
-                .map(accountState -> accountState.account().id().get());
-        }
-        catch (Exception e) {
-            log.error("Could not find account ID for username '{}'", authorUsername);
-            return Optional.empty();
-        }
+  protected Optional<Integer> getAccountId(String authorUsername) {
+    try {
+      return accountCache
+          .getByUsername(authorUsername)
+          .map(accountState -> accountState.account().id().get());
+    } catch (Exception e) {
+      log.error("Could not find account ID for username '{}'", authorUsername);
+      return Optional.empty();
     }
+  }
 
-    public Integer getNotNullAccountId(String authorUsername) {
-        return getAccountId(authorUsername).orElseThrow(() -> new NoSuchElementException(
-                String.format("Error retrieving '%s' account ID in Gerrit", authorUsername)));
-    }
+  public Integer getNotNullAccountId(String authorUsername) {
+    return getAccountId(authorUsername)
+        .orElseThrow(
+            () ->
+                new NoSuchElementException(
+                    String.format("Error retrieving '%s' account ID in Gerrit", authorUsername)));
+  }
 
-    private List<String> getAccountGroups(Integer accountId) {
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            List<GroupInfo> groups = config.getGerritApi().accounts().id(accountId).getGroups();
-            return groups.stream().map(g -> g.name).collect(toList());
-        }
-        catch (Exception e) {
-            log.error("Could not find groups for account ID {}", accountId);
-            return null;
-        }
+  private List<String> getAccountGroups(Integer accountId) {
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      List<GroupInfo> groups = config.getGerritApi().accounts().id(accountId).getGroups();
+      return groups.stream().map(g -> g.name).collect(toList());
+    } catch (Exception e) {
+      log.error("Could not find groups for account ID {}", accountId);
+      return null;
     }
+  }
 
-    private boolean isDisabledUserGroup(String authorUsername) {
-        List<String> enabledGroups = config.getEnabledGroups();
-        List<String> disabledGroups = config.getDisabledGroups();
-        if (enabledGroups.isEmpty() && disabledGroups.isEmpty()) {
-            return false;
-        }
-        Optional<Integer> accountId = getAccountId(authorUsername);
-        if (accountId.isEmpty()) {
-            return false;
-        }
-        List<String> accountGroups = getAccountGroups(accountId.orElse(-1));
-        if (accountGroups == null || accountGroups.isEmpty()) {
-            return false;
-        }
-        return !enabledGroups.contains(Configuration.ENABLED_GROUPS_ALL)
-                && enabledGroups.stream().noneMatch(accountGroups::contains)
-                || disabledGroups.stream().anyMatch(accountGroups::contains);
+  private boolean isDisabledUserGroup(String authorUsername) {
+    List<String> enabledGroups = config.getEnabledGroups();
+    List<String> disabledGroups = config.getDisabledGroups();
+    if (enabledGroups.isEmpty() && disabledGroups.isEmpty()) {
+      return false;
     }
+    Optional<Integer> accountId = getAccountId(authorUsername);
+    if (accountId.isEmpty()) {
+      return false;
+    }
+    List<String> accountGroups = getAccountGroups(accountId.orElse(-1));
+    if (accountGroups == null || accountGroups.isEmpty()) {
+      return false;
+    }
+    return !enabledGroups.contains(Configuration.ENABLED_GROUPS_ALL)
+            && enabledGroups.stream().noneMatch(accountGroups::contains)
+        || disabledGroups.stream().anyMatch(accountGroups::contains);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientBase.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientBase.java
index 0c5ed8e..21e9abb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientBase.java
@@ -1,20 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
+import java.util.HashMap;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.HashMap;
-
-
 @Slf4j
 public abstract class GerritClientBase extends ClientBase {
-    @Getter
-    protected HashMap<String, FileDiffProcessed> fileDiffsProcessed = new HashMap<>();
+  @Getter protected HashMap<String, FileDiffProcessed> fileDiffsProcessed = new HashMap<>();
 
-    public GerritClientBase(Configuration config) {
-        super(config);
-    }
+  public GerritClientBase(Configuration config) {
+    super(config);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientComments.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientComments.java
index 3c65805..18a6503 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientComments.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientComments.java
@@ -1,5 +1,25 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
+import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientDetail.toAuthor;
+import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientDetail.toDateString;
+import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_PATCH_SET_FILENAME;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TimeUtils.getTimeStamp;
+import static java.util.stream.Collectors.toList;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.server.account.AccountCache;
@@ -14,189 +34,184 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.CommentData;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 
-import static java.util.stream.Collectors.toList;
-
-import java.util.*;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TimeUtils.getTimeStamp;
-import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientDetail.toAuthor;
-import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientDetail.toDateString;
-import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_PATCH_SET_FILENAME;
-
 @Slf4j
 public class GerritClientComments extends GerritClientAccount {
-    private static final Integer MAX_SECS_GAP_BETWEEN_EVENT_AND_COMMENT = 2;
+  private static final Integer MAX_SECS_GAP_BETWEEN_EVENT_AND_COMMENT = 2;
 
-    private final ChangeSetData changeSetData;
-    private final HashMap<String, GerritComment> commentMap;
-    private final HashMap<String, GerritComment> patchSetCommentMap;
-    private final PluginDataHandlerProvider pluginDataHandlerProvider;
-    private final Localizer localizer;
+  private final ChangeSetData changeSetData;
+  private final HashMap<String, GerritComment> commentMap;
+  private final HashMap<String, GerritComment> patchSetCommentMap;
+  private final PluginDataHandlerProvider pluginDataHandlerProvider;
+  private final Localizer localizer;
 
-    private String authorUsername;
-    @Getter
-    private List<GerritComment> commentProperties;
+  private String authorUsername;
+  @Getter private List<GerritComment> commentProperties;
 
-    @VisibleForTesting
-    @Inject
-    public GerritClientComments(
-            Configuration config,
-            AccountCache accountCache,
-            ChangeSetData changeSetData,
-            PluginDataHandlerProvider pluginDataHandlerProvider,
-            Localizer localizer
-    ) {
-        super(config, accountCache);
-        this.changeSetData = changeSetData;
-        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
-        this.localizer = localizer;
-        commentProperties = new ArrayList<>();
-        commentMap = new HashMap<>();
-        patchSetCommentMap = new HashMap<>();
+  @VisibleForTesting
+  @Inject
+  public GerritClientComments(
+      Configuration config,
+      AccountCache accountCache,
+      ChangeSetData changeSetData,
+      PluginDataHandlerProvider pluginDataHandlerProvider,
+      Localizer localizer) {
+    super(config, accountCache);
+    this.changeSetData = changeSetData;
+    this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+    this.localizer = localizer;
+    commentProperties = new ArrayList<>();
+    commentMap = new HashMap<>();
+    patchSetCommentMap = new HashMap<>();
+  }
+
+  public CommentData getCommentData() {
+    return new CommentData(commentProperties, commentMap, patchSetCommentMap);
+  }
+
+  public boolean retrieveLastComments(GerritChange change) {
+    CommentAddedEvent commentAddedEvent = (CommentAddedEvent) change.getEvent();
+    authorUsername = commentAddedEvent.author.get().username;
+    log.debug("Found comments by '{}' on {}", authorUsername, change.getEventTimeStamp());
+    if (authorUsername.equals(config.getGerritUserName())) {
+      log.debug("These are the Bot's own comments, do not process them.");
+      return false;
     }
-
-    public CommentData getCommentData() {
-        return new CommentData(commentProperties, commentMap, patchSetCommentMap);
+    if (isDisabledUser(authorUsername)) {
+      log.info("Review of comments from user '{}' is disabled.", authorUsername);
+      return false;
     }
+    addLastComments(change);
 
-    public boolean retrieveLastComments(GerritChange change) {
-        CommentAddedEvent commentAddedEvent = (CommentAddedEvent) change.getEvent();
-        authorUsername = commentAddedEvent.author.get().username;
-        log.debug("Found comments by '{}' on {}", authorUsername, change.getEventTimeStamp());
-        if (authorUsername.equals(config.getGerritUserName())) {
-            log.debug("These are the Bot's own comments, do not process them.");
-            return false;
+    return !commentProperties.isEmpty();
+  }
+
+  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 {
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      Map<String, List<CommentInfo>> comments =
+          config
+              .getGerritApi()
+              .changes()
+              .id(
+                  change.getProjectName(),
+                  change.getBranchNameKey().shortName(),
+                  change.getChangeKey().get())
+              .commentsRequest()
+              .get();
+
+      // note that list of Map.Entry was used in order to keep the original response order
+      List<Map.Entry<String, List<GerritComment>>> lastCommentEntries =
+          comments.entrySet().stream()
+              .map(
+                  entry ->
+                      Map.entry(
+                          entry.getKey(),
+                          entry.getValue().stream()
+                              .map(GerritClientComments::toComment)
+                              .collect(toList())))
+              .collect(toList());
+
+      String latestChangeMessageId = null;
+      HashMap<String, List<GerritComment>> latestComments = new HashMap<>();
+      for (Map.Entry<String, List<GerritComment>> entry : lastCommentEntries) {
+        String filename = entry.getKey();
+        log.info("Commented filename: {}", filename);
+
+        List<GerritComment> commentsArray = entry.getValue();
+
+        for (GerritComment commentObject : commentsArray) {
+          commentObject.setFilename(filename);
+          String commentId = commentObject.getId();
+          String changeMessageId = commentObject.getChangeMessageId();
+          String commentAuthorUsername = commentObject.getAuthor().getUsername();
+          log.debug(
+              "Change Message Id: {} - Author: {}", latestChangeMessageId, commentAuthorUsername);
+          long updatedTimeStamp = getTimeStamp(commentObject.getUpdated());
+          if (commentAuthorUsername.equals(authorUsername)
+              && updatedTimeStamp
+                  >= change.getEventTimeStamp() - MAX_SECS_GAP_BETWEEN_EVENT_AND_COMMENT) {
+            log.debug("Found comment with updatedTimeStamp : {}", updatedTimeStamp);
+            latestChangeMessageId = changeMessageId;
+          }
+          latestComments
+              .computeIfAbsent(changeMessageId, k -> new ArrayList<>())
+              .add(commentObject);
+          commentMap.put(commentId, commentObject);
+          if (filename.equals(GERRIT_PATCH_SET_FILENAME)) {
+            patchSetCommentMap.put(changeMessageId, commentObject);
+          }
         }
-        if (isDisabledUser(authorUsername)) {
-            log.info("Review of comments from user '{}' is disabled.", authorUsername);
-            return false;
-        }
-        addLastComments(change);
+      }
 
-        return !commentProperties.isEmpty();
+      return latestComments.getOrDefault(latestChangeMessageId, null);
     }
+  }
 
-    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 {
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            Map<String, List<CommentInfo>> comments =
-                config
-                    .getGerritApi()
-                    .changes()
-                    .id(
-                        change.getProjectName(),
-                        change.getBranchNameKey().shortName(),
-                        change.getChangeKey().get())
-                    .commentsRequest()
-                    .get();
-
-            // note that list of Map.Entry was used in order to keep the original response order
-            List<Map.Entry<String, List<GerritComment>>> lastCommentEntries =
-                comments.entrySet().stream()
-                    .map(
-                        entry ->
-                            Map.entry(
-                                entry.getKey(),
-                                entry.getValue().stream()
-                                    .map(GerritClientComments::toComment)
-                                    .collect(toList())))
-                    .collect(toList());
-
-            String latestChangeMessageId = null;
-            HashMap<String, List<GerritComment>> latestComments = new HashMap<>();
-            for (Map.Entry<String, List<GerritComment>> entry : lastCommentEntries) {
-                String filename = entry.getKey();
-                log.info("Commented filename: {}", filename);
-
-                List<GerritComment> commentsArray = entry.getValue();
-
-                for (GerritComment commentObject : commentsArray) {
-                    commentObject.setFilename(filename);
-                    String commentId = commentObject.getId();
-                    String changeMessageId = commentObject.getChangeMessageId();
-                    String commentAuthorUsername = commentObject.getAuthor().getUsername();
-                    log.debug(
-                        "Change Message Id: {} - Author: {}", latestChangeMessageId, commentAuthorUsername);
-                    long updatedTimeStamp = getTimeStamp(commentObject.getUpdated());
-                    if (commentAuthorUsername.equals(authorUsername)
-                        && updatedTimeStamp
-                            >= change.getEventTimeStamp() - MAX_SECS_GAP_BETWEEN_EVENT_AND_COMMENT) {
-                      log.debug("Found comment with updatedTimeStamp : {}", updatedTimeStamp);
-                      latestChangeMessageId = changeMessageId;
-                    }
-                    latestComments
-                        .computeIfAbsent(changeMessageId, k -> new ArrayList<>())
-                        .add(commentObject);
-                    commentMap.put(commentId, commentObject);
-                    if (filename.equals(GERRIT_PATCH_SET_FILENAME)) {
-                        patchSetCommentMap.put(changeMessageId, commentObject);
-                    }
-                }
+  private void addLastComments(GerritChange change) {
+    ClientMessage clientMessage =
+        new ClientMessage(config, changeSetData, pluginDataHandlerProvider, localizer);
+    try {
+      List<GerritComment> latestComments = retrieveComments(change);
+      if (latestComments == null) {
+        return;
+      }
+      for (GerritComment latestComment : latestComments) {
+        String commentMessage = latestComment.getMessage();
+        if (clientMessage.isBotAddressed(commentMessage)) {
+          if (clientMessage.parseCommands(commentMessage, true)) {
+            if (clientMessage.isContainingHistoryCommand()) {
+              clientMessage.processHistoryCommand();
             }
-
-            return latestComments.getOrDefault(latestChangeMessageId, null);
+            commentProperties.clear();
+            return;
+          }
+          commentProperties.add(latestComment);
         }
+      }
+    } catch (Exception e) {
+      log.error("Error while retrieving last comments for change: {}", change.getFullChangeId(), e);
     }
+  }
 
-    private void addLastComments(GerritChange change) {
-        ClientMessage clientMessage = new ClientMessage(config, changeSetData, pluginDataHandlerProvider, localizer);
-        try {
-            List<GerritComment> latestComments = retrieveComments(change);
-            if (latestComments == null) {
-                return;
-            }
-            for (GerritComment latestComment : latestComments) {
-                String commentMessage = latestComment.getMessage();
-                if (clientMessage.isBotAddressed(commentMessage)) {
-                    if (clientMessage.parseCommands(commentMessage, true)) {
-                        if (clientMessage.isContainingHistoryCommand()) {
-                            clientMessage.processHistoryCommand();
-                        }
-                        commentProperties.clear();
-                        return;
-                    }
-                    commentProperties.add(latestComment);
-                }
-            }
-        } catch (Exception e) {
-            log.error("Error while retrieving last comments for change: {}", change.getFullChangeId(), e);
-        }
-    }
-
-    private static GerritComment toComment(CommentInfo comment) {
-        GerritComment gerritComment = new GerritComment();
-        gerritComment.setAuthor(toAuthor(comment.author));
-        gerritComment.setChangeMessageId(comment.changeMessageId);
-        gerritComment.setUnresolved(comment.unresolved);
-        gerritComment.setPatchSet(comment.patchSet);
-        gerritComment.setId(comment.id);
-        gerritComment.setTag(comment.tag);
-        gerritComment.setLine(comment.line);
-        Optional.ofNullable(comment.range)
-            .ifPresent(
-                range ->
-                    gerritComment.setRange(
-                        GerritCodeRange.builder()
-                            .startLine(range.startLine)
-                            .endLine(range.endLine)
-                            .startCharacter(range.startCharacter)
-                            .endCharacter(range.endCharacter)
-                            .build()));
-        gerritComment.setInReplyTo(comment.inReplyTo);
-        Optional.ofNullable(comment.updated)
-            .ifPresent(updated -> gerritComment.setUpdated(toDateString(updated)));
-        gerritComment.setMessage(comment.message);
-        gerritComment.setCommitId(comment.commitId);
-        return gerritComment;
-    }
+  private static GerritComment toComment(CommentInfo comment) {
+    GerritComment gerritComment = new GerritComment();
+    gerritComment.setAuthor(toAuthor(comment.author));
+    gerritComment.setChangeMessageId(comment.changeMessageId);
+    gerritComment.setUnresolved(comment.unresolved);
+    gerritComment.setPatchSet(comment.patchSet);
+    gerritComment.setId(comment.id);
+    gerritComment.setTag(comment.tag);
+    gerritComment.setLine(comment.line);
+    Optional.ofNullable(comment.range)
+        .ifPresent(
+            range ->
+                gerritComment.setRange(
+                    GerritCodeRange.builder()
+                        .startLine(range.startLine)
+                        .endLine(range.endLine)
+                        .startCharacter(range.startCharacter)
+                        .endCharacter(range.endCharacter)
+                        .build()));
+    gerritComment.setInReplyTo(comment.inReplyTo);
+    Optional.ofNullable(comment.updated)
+        .ifPresent(updated -> gerritComment.setUpdated(toDateString(updated)));
+    gerritComment.setMessage(comment.message);
+    gerritComment.setCommitId(comment.commitId);
+    return gerritComment;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientDetail.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientDetail.java
index 7d2630a..0366efc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientDetail.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientDetail.java
@@ -1,5 +1,22 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
+import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toList;
+
 import com.google.gerrit.entities.LabelId;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -12,11 +29,6 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPatchSetDetail;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPermittedVotingRange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.extern.slf4j.Slf4j;
-
-import static java.util.Collections.emptyList;
-import static java.util.stream.Collectors.toList;
-
 import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
 import java.util.List;
@@ -25,147 +37,152 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.TimeZone;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class GerritClientDetail {
-    private static final SimpleDateFormat DATE_FORMAT = newFormat();
+  private static final SimpleDateFormat DATE_FORMAT = newFormat();
 
-    private GerritPatchSetDetail gerritPatchSetDetail;
-    private final int gptAccountId;
-    private final Configuration config;
+  private GerritPatchSetDetail gerritPatchSetDetail;
+  private final int gptAccountId;
+  private final Configuration config;
 
-    public GerritClientDetail(Configuration config, ChangeSetData changeSetData) {
-        this.gptAccountId = changeSetData.getGptAccountId();
-        this.config = config;
+  public GerritClientDetail(Configuration config, ChangeSetData changeSetData) {
+    this.gptAccountId = changeSetData.getGptAccountId();
+    this.config = config;
+  }
+
+  public List<GerritComment> getMessages(GerritChange change) {
+    loadPatchSetDetail(change);
+    return gerritPatchSetDetail.getMessages();
+  }
+
+  public boolean isWorkInProgress(GerritChange change) {
+    loadPatchSetDetail(change);
+    return gerritPatchSetDetail.getWorkInProgress() != null
+        && gerritPatchSetDetail.getWorkInProgress();
+  }
+
+  public GerritPermittedVotingRange getPermittedVotingRange(GerritChange change) {
+    loadPatchSetDetail(change);
+    List<GerritPatchSetDetail.Permission> permissions =
+        gerritPatchSetDetail.getLabels().getCodeReview().getAll();
+    if (permissions == null) {
+      log.debug("No limitations on the AICodeReview voting range were detected");
+      return null;
     }
-
-    public List<GerritComment> getMessages(GerritChange change) {
-        loadPatchSetDetail(change);
-        return gerritPatchSetDetail.getMessages();
+    for (GerritPatchSetDetail.Permission permission : permissions) {
+      if (permission.getAccountId() == gptAccountId) {
+        log.debug(
+            "PatchSet voting range detected for AICodeReview user: {}",
+            permission.getPermittedVotingRange());
+        return permission.getPermittedVotingRange();
+      }
     }
+    return null;
+  }
 
-    public boolean isWorkInProgress(GerritChange change) {
-        loadPatchSetDetail(change);
-        return gerritPatchSetDetail.getWorkInProgress() != null && gerritPatchSetDetail.getWorkInProgress();
+  private void loadPatchSetDetail(GerritChange change) {
+    if (gerritPatchSetDetail != null) {
+      return;
     }
-
-    public GerritPermittedVotingRange getPermittedVotingRange(GerritChange change) {
-        loadPatchSetDetail(change);
-        List<GerritPatchSetDetail.Permission> permissions = gerritPatchSetDetail.getLabels().getCodeReview().getAll();
-        if (permissions == null) {
-            log.debug("No limitations on the AICodeReview voting range were detected");
-            return null;
-        }
-        for (GerritPatchSetDetail.Permission permission : permissions) {
-            if (permission.getAccountId() == gptAccountId) {
-                log.debug("PatchSet voting range detected for AICodeReview user: {}", permission.getPermittedVotingRange());
-                return permission.getPermittedVotingRange();
-            }
-        }
-        return null;
+    try {
+      gerritPatchSetDetail = getReviewDetail(change);
+    } catch (Exception e) {
+      log.error("Error retrieving PatchSet details", e);
     }
+  }
 
-    private void loadPatchSetDetail(GerritChange change) {
-        if (gerritPatchSetDetail != null) {
-            return;
-        }
-        try {
-            gerritPatchSetDetail = getReviewDetail(change);
-        }
-        catch (Exception e) {
-            log.error("Error retrieving PatchSet details", e);
-        }
+  private GerritPatchSetDetail getReviewDetail(GerritChange change) throws Exception {
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      ChangeInfo info =
+          config
+              .getGerritApi()
+              .changes()
+              .id(
+                  change.getProjectName(),
+                  change.getBranchNameKey().shortName(),
+                  change.getChangeKey().get())
+              .get();
+
+      GerritPatchSetDetail detail = new GerritPatchSetDetail();
+      detail.setWorkInProgress(info.workInProgress);
+      Optional.ofNullable(info.labels)
+          .map(Map::entrySet)
+          .map(Set::stream)
+          .flatMap(
+              labels ->
+                  labels
+                      .filter(label -> LabelId.CODE_REVIEW.equals(label.getKey()))
+                      .map(GerritClientDetail::toLabels)
+                      .findAny())
+          .ifPresent(detail::setLabels);
+      Optional.ofNullable(info.messages)
+          .map(messages -> messages.stream().map(GerritClientDetail::toComment).collect(toList()))
+          .ifPresent(detail::setMessages);
+
+      return detail;
     }
+  }
 
-    private GerritPatchSetDetail getReviewDetail(GerritChange change) throws Exception {
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-          ChangeInfo info =
-              config
-                  .getGerritApi()
-                  .changes()
-                  .id(change.getProjectName(), change.getBranchNameKey().shortName(), change.getChangeKey().get())
-                  .get();
+  private static GerritPatchSetDetail.Labels toLabels(Entry<String, LabelInfo> label) {
+    List<GerritPatchSetDetail.Permission> permissions =
+        Optional.ofNullable(label.getValue().all)
+            .map(all -> all.stream().map(GerritClientDetail::toPermission).collect(toList()))
+            .orElse(emptyList());
+    GerritPatchSetDetail.CodeReview codeReview = new GerritPatchSetDetail.CodeReview();
+    codeReview.setAll(permissions);
+    GerritPatchSetDetail.Labels labels = new GerritPatchSetDetail.Labels();
+    labels.setCodeReview(codeReview);
+    return labels;
+  }
 
-          GerritPatchSetDetail detail = new GerritPatchSetDetail();
-          detail.setWorkInProgress(info.workInProgress);
-          Optional.ofNullable(info.labels)
-              .map(Map::entrySet)
-              .map(Set::stream)
-              .flatMap(
-                  labels ->
-                      labels
-                          .filter(label -> LabelId.CODE_REVIEW.equals(label.getKey()))
-                          .map(GerritClientDetail::toLabels)
-                          .findAny())
-              .ifPresent(detail::setLabels);
-          Optional.ofNullable(info.messages)
-              .map(messages -> messages.stream().map(GerritClientDetail::toComment).collect(toList()))
-              .ifPresent(detail::setMessages);
+  private static GerritPatchSetDetail.Permission toPermission(ApprovalInfo value) {
+    GerritPatchSetDetail.Permission permission = new GerritPatchSetDetail.Permission();
+    permission.setValue(value.value);
+    Optional.ofNullable(value.date).ifPresent(date -> permission.setDate(toDateString(date)));
+    Optional.ofNullable(value.permittedVotingRange)
+        .ifPresent(
+            permittedVotingRange -> {
+              GerritPermittedVotingRange range = new GerritPermittedVotingRange();
+              range.setMin(permittedVotingRange.min);
+              range.setMax(permittedVotingRange.max);
+              permission.setPermittedVotingRange(range);
+            });
+    permission.setAccountId(value._accountId);
+    return permission;
+  }
 
-          return detail;
-        }
-    }
+  private static GerritComment toComment(ChangeMessageInfo message) {
+    GerritComment comment = new GerritComment();
+    Optional.ofNullable(message.author).ifPresent(author -> comment.setAuthor(toAuthor(author)));
+    comment.setId(message.id);
+    comment.setTag(message.tag);
+    Optional.ofNullable(message.date).ifPresent(date -> comment.setDate(toDateString(date)));
+    comment.setMessage(message.message);
+    comment.setPatchSet(message._revisionNumber);
+    return comment;
+  }
 
-    private static GerritPatchSetDetail.Labels toLabels(Entry<String, LabelInfo> label) {
-        List<GerritPatchSetDetail.Permission> permissions =
-            Optional.ofNullable(label.getValue().all)
-                .map(all -> all.stream().map(GerritClientDetail::toPermission).collect(toList()))
-                .orElse(emptyList());
-        GerritPatchSetDetail.CodeReview codeReview = new GerritPatchSetDetail.CodeReview();
-        codeReview.setAll(permissions);
-        GerritPatchSetDetail.Labels labels = new GerritPatchSetDetail.Labels();
-        labels.setCodeReview(codeReview);
-        return labels;
-    }
+  static GerritComment.Author toAuthor(AccountInfo authorInfo) {
+    GerritComment.Author author = new GerritComment.Author();
+    author.setAccountId(authorInfo._accountId);
+    author.setName(authorInfo.name);
+    author.setDisplayName(author.getDisplayName());
+    author.setEmail(authorInfo.email);
+    author.setUsername(authorInfo.username);
+    return author;
+  }
 
-    private static GerritPatchSetDetail.Permission toPermission(ApprovalInfo value) {
-        GerritPatchSetDetail.Permission permission = new GerritPatchSetDetail.Permission();
-        permission.setValue(value.value);
-        Optional.ofNullable(value.date).ifPresent(date -> permission.setDate(toDateString(date)));
-        Optional.ofNullable(value.permittedVotingRange)
-            .ifPresent(
-                permittedVotingRange -> {
-                  GerritPermittedVotingRange range = new GerritPermittedVotingRange();
-                  range.setMin(permittedVotingRange.min);
-                  range.setMax(permittedVotingRange.max);
-                  permission.setPermittedVotingRange(range);
-                });
-        permission.setAccountId(value._accountId);
-        return permission;
-    }
+  /** Date format copied from <b>com.google.gerrit.json.SqlTimestampDeserializer</b> */
+  static String toDateString(Timestamp input) {
+    return DATE_FORMAT.format(input) + "000000";
+  }
 
-    private static GerritComment toComment(ChangeMessageInfo message) {
-        GerritComment comment = new GerritComment();
-        Optional.ofNullable(message.author).ifPresent(author -> comment.setAuthor(toAuthor(author)));
-        comment.setId(message.id);
-        comment.setTag(message.tag);
-        Optional.ofNullable(message.date).ifPresent(date -> comment.setDate(toDateString(date)));
-        comment.setMessage(message.message);
-        comment.setPatchSet(message._revisionNumber);
-        return comment;
-    }
-
-    static GerritComment.Author toAuthor(AccountInfo authorInfo ) {
-        GerritComment.Author author = new GerritComment.Author();
-        author.setAccountId(authorInfo._accountId);
-        author.setName(authorInfo.name);
-        author.setDisplayName(author.getDisplayName());
-        author.setEmail(authorInfo.email);
-        author.setUsername(authorInfo.username);
-        return author;
-    }
-
-    /**
-     * Date format copied from <b>com.google.gerrit.json.SqlTimestampDeserializer</b>
-     */
-    static String toDateString(Timestamp input) {
-        return DATE_FORMAT.format(input) + "000000";
-    }
-
-    private static SimpleDateFormat newFormat() {
-        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-        f.setTimeZone(TimeZone.getTimeZone("UTC"));
-        f.setLenient(true);
-        return f;
-    }
+  private static SimpleDateFormat newFormat() {
+    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+    f.setTimeZone(TimeZone.getTimeZone("UTC"));
+    f.setLenient(true);
+    return f;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientFacade.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientFacade.java
index 0278794..bee18aa 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientFacade.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -8,73 +22,71 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPermittedVotingRange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.HashMap;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class GerritClientFacade {
-    private final ChangeSetData changeSetData;
-    private final GerritClientDetail gerritClientDetail;
-    private final GerritClientComments gerritClientComments;
-    private final GerritClientPatchSet gerritClientPatchSet;
+  private final ChangeSetData changeSetData;
+  private final GerritClientDetail gerritClientDetail;
+  private final GerritClientComments gerritClientComments;
+  private final GerritClientPatchSet gerritClientPatchSet;
 
-    @VisibleForTesting
-    @Inject
-    public GerritClientFacade(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientComments gerritClientComments,
-            GerritClientPatchSet gerritClientPatchSet) {
-        gerritClientDetail = new GerritClientDetail(config, changeSetData);
-        this.gerritClientPatchSet = gerritClientPatchSet;
-        this.changeSetData = changeSetData;
-        this.gerritClientComments = gerritClientComments;
-    }
+  @VisibleForTesting
+  @Inject
+  public GerritClientFacade(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientComments gerritClientComments,
+      GerritClientPatchSet gerritClientPatchSet) {
+    gerritClientDetail = new GerritClientDetail(config, changeSetData);
+    this.gerritClientPatchSet = gerritClientPatchSet;
+    this.changeSetData = changeSetData;
+    this.gerritClientComments = gerritClientComments;
+  }
 
-    public GerritPermittedVotingRange getPermittedVotingRange(GerritChange change) {
-        return gerritClientDetail.getPermittedVotingRange(change);
-    }
+  public GerritPermittedVotingRange getPermittedVotingRange(GerritChange change) {
+    return gerritClientDetail.getPermittedVotingRange(change);
+  }
 
-    public String getPatchSet(GerritChange change) throws Exception {
-        return gerritClientPatchSet.getPatchSet(changeSetData, change);
-    }
+  public String getPatchSet(GerritChange change) throws Exception {
+    return gerritClientPatchSet.getPatchSet(changeSetData, change);
+  }
 
-    public boolean isDisabledUser(String authorUsername) {
-        return gerritClientPatchSet.isDisabledUser(authorUsername);
-    }
+  public boolean isDisabledUser(String authorUsername) {
+    return gerritClientPatchSet.isDisabledUser(authorUsername);
+  }
 
-    public boolean isDisabledTopic(String topic) {
-        return gerritClientPatchSet.isDisabledTopic(topic);
-    }
+  public boolean isDisabledTopic(String topic) {
+    return gerritClientPatchSet.isDisabledTopic(topic);
+  }
 
-    public boolean isWorkInProgress(GerritChange change) {
-        return gerritClientDetail.isWorkInProgress(change);
-    }
+  public boolean isWorkInProgress(GerritChange change) {
+    return gerritClientDetail.isWorkInProgress(change);
+  }
 
-    public HashMap<String, FileDiffProcessed> getFileDiffsProcessed() {
-        return gerritClientPatchSet.getFileDiffsProcessed();
-    }
+  public HashMap<String, FileDiffProcessed> getFileDiffsProcessed() {
+    return gerritClientPatchSet.getFileDiffsProcessed();
+  }
 
-    public Integer getNotNullAccountId(String authorUsername) {
-        return gerritClientPatchSet.getNotNullAccountId(authorUsername);
-    }
+  public Integer getNotNullAccountId(String authorUsername) {
+    return gerritClientPatchSet.getNotNullAccountId(authorUsername);
+  }
 
-    public boolean retrieveLastComments(GerritChange change) {
-        return gerritClientComments.retrieveLastComments(change);
-    }
+  public boolean retrieveLastComments(GerritChange change) {
+    return gerritClientComments.retrieveLastComments(change);
+  }
 
-    public void retrievePatchSetInfo(GerritChange change) {
-        gerritClientComments.retrieveAllComments(change);
-        gerritClientPatchSet.retrieveRevisionBase(change);
-    }
+  public void retrievePatchSetInfo(GerritChange change) {
+    gerritClientComments.retrieveAllComments(change);
+    gerritClientPatchSet.retrieveRevisionBase(change);
+  }
 
-    public GerritClientData getClientData(GerritChange change) {
-        return new GerritClientData(
-                gerritClientPatchSet.getFileDiffsProcessed(),
-                gerritClientDetail.getMessages(change),
-                gerritClientComments.getCommentData(),
-                gerritClientPatchSet.getRevisionBase()
-        );
-    }
+  public GerritClientData getClientData(GerritChange change) {
+    return new GerritClientData(
+        gerritClientPatchSet.getFileDiffsProcessed(),
+        gerritClientDetail.getMessages(change),
+        gerritClientComments.getCommentData(),
+        gerritClientPatchSet.getRevisionBase());
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientPatchSet.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientPatchSet.java
index 2450da0..ad091c4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientPatchSet.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientPatchSet.java
@@ -1,8 +1,22 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.matchesExtensionList;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getNoEscapedGson;
+import static java.util.stream.Collectors.toList;
 
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -15,121 +29,120 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPatchSetFileDiff;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritReviewFileDiff;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.matchesExtensionList;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getNoEscapedGson;
-import static java.util.stream.Collectors.toList;
-
 @Slf4j
 public class GerritClientPatchSet extends GerritClientAccount {
-    protected final List<String> diffs;
+  protected final List<String> diffs;
 
-    @Getter
-    protected Integer revisionBase = 0;
+  @Getter protected Integer revisionBase = 0;
 
-    private boolean isCommitMessage;
+  private boolean isCommitMessage;
 
-    public GerritClientPatchSet(Configuration config, AccountCache accountCache) {
-        super(config, accountCache);
-        diffs = new ArrayList<>();
+  public GerritClientPatchSet(Configuration config, AccountCache accountCache) {
+    super(config, accountCache);
+    diffs = new ArrayList<>();
+  }
+
+  public void retrieveRevisionBase(GerritChange change) {
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      ChangeInfo changeInfo =
+          config
+              .getGerritApi()
+              .changes()
+              .id(
+                  change.getProjectName(),
+                  change.getBranchNameKey().shortName(),
+                  change.getChangeKey().get())
+              .get(ListChangesOption.ALL_REVISIONS);
+      revisionBase =
+          Optional.ofNullable(changeInfo)
+              .map(info -> info.revisions)
+              .map(revisions -> revisions.size() - 1)
+              .orElse(0);
+    } catch (Exception e) {
+      log.error(
+          "Could not retrieve revisions for PatchSet with fullChangeId: {}",
+          change.getFullChangeId(),
+          e);
+      revisionBase = 0;
     }
+  }
 
-    public void retrieveRevisionBase(GerritChange change) {
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            ChangeInfo changeInfo =
-                config
-                    .getGerritApi()
-                    .changes()
-                    .id(
-                        change.getProjectName(),
-                        change.getBranchNameKey().shortName(),
-                        change.getChangeKey().get())
-                    .get(ListChangesOption.ALL_REVISIONS);
-            revisionBase =
-                Optional.ofNullable(changeInfo)
-                    .map(info -> info.revisions)
-                    .map(revisions -> revisions.size() - 1)
-                    .orElse(0);
+  protected int getChangeSetRevisionBase(ChangeSetData changeSetData) {
+    return isChangeSetBased(changeSetData) ? 0 : revisionBase;
+  }
+
+  protected void retrieveFileDiff(GerritChange change, List<String> files, int revisionBase)
+      throws Exception {
+    List<String> enabledFileExtensions = config.getEnabledFileExtensions();
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      for (String filename : files) {
+        isCommitMessage = filename.equals("/COMMIT_MSG");
+        if (!isCommitMessage && !matchesExtensionList(filename, enabledFileExtensions)) {
+          continue;
         }
-        catch (Exception e) {
-            log.error("Could not retrieve revisions for PatchSet with fullChangeId: {}", change.getFullChangeId(), e);
-            revisionBase = 0;
-        }
+        DiffInfo diff =
+            config
+                .getGerritApi()
+                .changes()
+                .id(
+                    change.getProjectName(),
+                    change.getBranchNameKey().shortName(),
+                    change.getChangeKey().get())
+                .current()
+                .file(filename)
+                .diff(revisionBase);
+        processFileDiff(filename, diff);
+      }
     }
+  }
 
-    protected int getChangeSetRevisionBase(ChangeSetData changeSetData) {
-        return isChangeSetBased(changeSetData) ? 0 : revisionBase;
-    }
+  private boolean isChangeSetBased(ChangeSetData changeSetData) {
+    return !changeSetData.getForcedReviewLastPatchSet();
+  }
 
-    protected void retrieveFileDiff(GerritChange change, List<String> files, int revisionBase) throws Exception {
-        List<String> enabledFileExtensions = config.getEnabledFileExtensions();
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            for (String filename : files) {
-                isCommitMessage = filename.equals("/COMMIT_MSG");
-                if (!isCommitMessage && !matchesExtensionList(filename, enabledFileExtensions)) {
-                    continue;
-                }
-                DiffInfo diff =
-                        config
-                                .getGerritApi()
-                                .changes()
-                                .id(
-                                        change.getProjectName(),
-                                        change.getBranchNameKey().shortName(),
-                                        change.getChangeKey().get())
-                                .current()
-                                .file(filename)
-                                .diff(revisionBase);
-                processFileDiff(filename, diff);
-            }
-        }
-    }
+  private void processFileDiff(String filename, DiffInfo diff) {
+    log.debug("FileDiff content processed: {}", filename);
 
-    private boolean isChangeSetBased(ChangeSetData changeSetData) {
-        return !changeSetData.getForcedReviewLastPatchSet();
-    }
+    GerritPatchSetFileDiff gerritPatchSetFileDiff = new GerritPatchSetFileDiff();
+    Optional.ofNullable(diff.metaA)
+        .ifPresent(meta -> gerritPatchSetFileDiff.setMetaA(GerritClientPatchSet.toMeta(meta)));
+    Optional.ofNullable(diff.metaB)
+        .ifPresent(meta -> gerritPatchSetFileDiff.setMetaB(GerritClientPatchSet.toMeta(meta)));
+    Optional.ofNullable(diff.content)
+        .ifPresent(
+            content ->
+                gerritPatchSetFileDiff.setContent(
+                    content.stream().map(GerritClientPatchSet::toContent).collect(toList())));
 
-    private void processFileDiff(String filename, DiffInfo diff) {
-        log.debug("FileDiff content processed: {}", filename);
+    // Initialize the reduced file diff for the Gerrit review with fields `meta_a` and `meta_b`
+    GerritReviewFileDiff gerritReviewFileDiff =
+        new GerritReviewFileDiff(
+            gerritPatchSetFileDiff.getMetaA(), gerritPatchSetFileDiff.getMetaB());
+    FileDiffProcessed fileDiffProcessed =
+        new FileDiffProcessed(config, isCommitMessage, gerritPatchSetFileDiff);
+    fileDiffsProcessed.put(filename, fileDiffProcessed);
+    gerritReviewFileDiff.setContent(fileDiffProcessed.getReviewDiffContent());
+    diffs.add(getNoEscapedGson().toJson(gerritReviewFileDiff));
+  }
 
-        GerritPatchSetFileDiff gerritPatchSetFileDiff = new GerritPatchSetFileDiff();
-        Optional.ofNullable(diff.metaA)
-                .ifPresent(
-                        meta -> gerritPatchSetFileDiff.setMetaA(GerritClientPatchSet.toMeta(meta)));
-        Optional.ofNullable(diff.metaB)
-                .ifPresent(
-                        meta -> gerritPatchSetFileDiff.setMetaB(GerritClientPatchSet.toMeta(meta)));
-        Optional.ofNullable(diff.content)
-                .ifPresent(
-                        content ->
-                                gerritPatchSetFileDiff.setContent(
-                                        content.stream()
-                                                .map(GerritClientPatchSet::toContent)
-                                                .collect(toList())));
+  protected static GerritFileDiff.Meta toMeta(DiffInfo.FileMeta input) {
+    GerritFileDiff.Meta meta = new GerritFileDiff.Meta();
+    meta.setContentType(input.contentType);
+    meta.setName(input.name);
+    return meta;
+  }
 
-        // Initialize the reduced file diff for the Gerrit review with fields `meta_a` and `meta_b`
-        GerritReviewFileDiff gerritReviewFileDiff = new GerritReviewFileDiff(gerritPatchSetFileDiff.getMetaA(),
-                gerritPatchSetFileDiff.getMetaB());
-        FileDiffProcessed fileDiffProcessed = new FileDiffProcessed(config, isCommitMessage, gerritPatchSetFileDiff);
-        fileDiffsProcessed.put(filename, fileDiffProcessed);
-        gerritReviewFileDiff.setContent(fileDiffProcessed.getReviewDiffContent());
-        diffs.add(getNoEscapedGson().toJson(gerritReviewFileDiff));
-    }
-
-    protected static GerritFileDiff.Meta toMeta(DiffInfo.FileMeta input) {
-        GerritFileDiff.Meta meta = new GerritFileDiff.Meta();
-        meta.setContentType(input.contentType);
-        meta.setName(input.name);
-        return meta;
-    }
-
-    protected static GerritPatchSetFileDiff.Content toContent(DiffInfo.ContentEntry input) {
-        GerritPatchSetFileDiff.Content content = new GerritPatchSetFileDiff.Content();
-        content.a = input.a;
-        content.b = input.b;
-        content.ab = input.ab;
-        return content;
-    }
+  protected static GerritPatchSetFileDiff.Content toContent(DiffInfo.ContentEntry input) {
+    GerritPatchSetFileDiff.Content content = new GerritPatchSetFileDiff.Content();
+    content.a = input.a;
+    content.b = input.b;
+    content.ab = input.ab;
+    return content;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientReview.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientReview.java
index 60e5c99..bb09bdf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/gerrit/GerritClientReview.java
@@ -1,12 +1,29 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit;
 
+import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.MessageSanitizer.sanitizeAIChatMessage;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithDoubleNewLine;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
-import com.google.gerrit.extensions.client.Comment;
 import com.google.gerrit.entities.LabelId;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
 import com.google.gerrit.extensions.api.changes.ReviewResult;
+import com.google.gerrit.extensions.client.Comment;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.inject.Inject;
@@ -17,141 +34,136 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.messages.DebugCodeBlocksDynamicSettings;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.review.ReviewBatch;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-
-import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.MessageSanitizer.sanitizeAIChatMessage;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithDoubleNewLine;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class GerritClientReview extends GerritClientAccount {
-    private final PluginDataHandlerProvider pluginDataHandlerProvider;
-    private final Localizer localizer;
-    private final DebugCodeBlocksDynamicSettings debugCodeBlocksDynamicSettings;
+  private final PluginDataHandlerProvider pluginDataHandlerProvider;
+  private final Localizer localizer;
+  private final DebugCodeBlocksDynamicSettings debugCodeBlocksDynamicSettings;
 
-    @VisibleForTesting
-    @Inject
-    public GerritClientReview(
-            Configuration config,
-            AccountCache accountCache,
-            PluginDataHandlerProvider pluginDataHandlerProvider,
-            Localizer localizer
-    ) {
-        super(config, accountCache);
-        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
-        this.localizer = localizer;
-        debugCodeBlocksDynamicSettings = new DebugCodeBlocksDynamicSettings(localizer);
+  @VisibleForTesting
+  @Inject
+  public GerritClientReview(
+      Configuration config,
+      AccountCache accountCache,
+      PluginDataHandlerProvider pluginDataHandlerProvider,
+      Localizer localizer) {
+    super(config, accountCache);
+    this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+    this.localizer = localizer;
+    debugCodeBlocksDynamicSettings = new DebugCodeBlocksDynamicSettings(localizer);
+  }
+
+  public void setReview(
+      GerritChange change,
+      List<ReviewBatch> reviewBatches,
+      ChangeSetData changeSetData,
+      Integer reviewScore)
+      throws Exception {
+    ReviewInput reviewInput = buildReview(reviewBatches, changeSetData, reviewScore);
+    if (reviewInput.comments == null && reviewInput.message == null) {
+      return;
     }
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      ReviewResult result =
+          config
+              .getGerritApi()
+              .changes()
+              .id(
+                  change.getProjectName(),
+                  change.getBranchNameKey().shortName(),
+                  change.getChangeKey().get())
+              .current()
+              .review(reviewInput);
 
-    public void setReview(
-            GerritChange change,
-            List<ReviewBatch> reviewBatches,
-            ChangeSetData changeSetData,
-            Integer reviewScore
-    ) throws Exception {
-        ReviewInput reviewInput = buildReview(reviewBatches, changeSetData, reviewScore);
-        if (reviewInput.comments == null && reviewInput.message == null) {
-            return;
-        }
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            ReviewResult result = config
-                .getGerritApi()
-                .changes()
-                .id(
-                    change.getProjectName(),
-                    change.getBranchNameKey().shortName(),
-                    change.getChangeKey().get())
-                .current()
-                .review(reviewInput);
-
-            if (!Strings.isNullOrEmpty(result.error)) {
-              log.error("Review setting failed with status code: {}", result.error);
-            }
-        }
+      if (!Strings.isNullOrEmpty(result.error)) {
+        log.error("Review setting failed with status code: {}", result.error);
+      }
     }
+  }
 
-    public void setReview(
-            GerritChange change,
-            List<ReviewBatch> reviewBatches,
-            ChangeSetData changeSetData
-    ) throws Exception {
-        setReview(change, reviewBatches, changeSetData, null);
-    }
+  public void setReview(
+      GerritChange change, List<ReviewBatch> reviewBatches, ChangeSetData changeSetData)
+      throws Exception {
+    setReview(change, reviewBatches, changeSetData, null);
+  }
 
-    private ReviewInput buildReview(List<ReviewBatch> reviewBatches, ChangeSetData changeSetData, Integer reviewScore) {
-        ReviewInput reviewInput = ReviewInput.create();
-        Map<String, List<CommentInput>> comments = new HashMap<>();
-        String systemMessage = localizer.getText("message.empty.review");
-        if (changeSetData.getReviewSystemMessage() != null) {
-            systemMessage = changeSetData.getReviewSystemMessage();
-        }
-        else if (!changeSetData.shouldHideAICodeReview()) {
-            comments = getReviewComments(reviewBatches);
-            if (reviewScore != null) {
-                reviewInput.label(LabelId.CODE_REVIEW, reviewScore);
-            }
-        }
-        updateSystemMessage(reviewInput, comments.isEmpty(), systemMessage);
-        if (!comments.isEmpty()) {
-            reviewInput.comments = comments;
-        }
-        return reviewInput;
+  private ReviewInput buildReview(
+      List<ReviewBatch> reviewBatches, ChangeSetData changeSetData, Integer reviewScore) {
+    ReviewInput reviewInput = ReviewInput.create();
+    Map<String, List<CommentInput>> comments = new HashMap<>();
+    String systemMessage = localizer.getText("message.empty.review");
+    if (changeSetData.getReviewSystemMessage() != null) {
+      systemMessage = changeSetData.getReviewSystemMessage();
+    } else if (!changeSetData.shouldHideAICodeReview()) {
+      comments = getReviewComments(reviewBatches);
+      if (reviewScore != null) {
+        reviewInput.label(LabelId.CODE_REVIEW, reviewScore);
+      }
     }
+    updateSystemMessage(reviewInput, comments.isEmpty(), systemMessage);
+    if (!comments.isEmpty()) {
+      reviewInput.comments = comments;
+    }
+    return reviewInput;
+  }
 
-    private void updateSystemMessage(ReviewInput reviewInput, boolean emptyComments, String systemMessage) {
-        List<String> messages = new ArrayList<>();
-        Map<String, String> dynamicConfig = new DynamicConfiguration(pluginDataHandlerProvider).getDynamicConfig();
-        if (dynamicConfig != null && !dynamicConfig.isEmpty()) {
-            messages.add(debugCodeBlocksDynamicSettings.getDebugCodeBlock(dynamicConfig));
-        }
-        if (emptyComments) {
-            messages.add(localizer.getText("system.message.prefix") + ' ' + systemMessage);
-        }
-        if (!messages.isEmpty()) {
-            reviewInput.message(joinWithDoubleNewLine(messages));
-        }
+  private void updateSystemMessage(
+      ReviewInput reviewInput, boolean emptyComments, String systemMessage) {
+    List<String> messages = new ArrayList<>();
+    Map<String, String> dynamicConfig =
+        new DynamicConfiguration(pluginDataHandlerProvider).getDynamicConfig();
+    if (dynamicConfig != null && !dynamicConfig.isEmpty()) {
+      messages.add(debugCodeBlocksDynamicSettings.getDebugCodeBlock(dynamicConfig));
     }
+    if (emptyComments) {
+      messages.add(localizer.getText("system.message.prefix") + ' ' + systemMessage);
+    }
+    if (!messages.isEmpty()) {
+      reviewInput.message(joinWithDoubleNewLine(messages));
+    }
+  }
 
-    private Map<String, List<CommentInput>> getReviewComments(List<ReviewBatch> reviewBatches) {
-        Map<String, List<CommentInput>> comments = new HashMap<>();
-        for (ReviewBatch reviewBatch : reviewBatches) {
-            String message = sanitizeAIChatMessage(reviewBatch.getContent());
-            if (message.trim().isEmpty()) {
-                log.info("Empty message from review not submitted.");
-                continue;
-            }
-            boolean unresolved;
-            String filename = reviewBatch.getFilename();
-            List<CommentInput> filenameComments = comments.getOrDefault(filename, new ArrayList<>());
-            CommentInput filenameComment = new CommentInput();
-            filenameComment.message = message;
-            if (reviewBatch.getLine() != null || reviewBatch.getRange() != null) {
-                filenameComment.line = reviewBatch.getLine();
-                Optional.ofNullable(reviewBatch.getRange())
-                        .ifPresent(
-                                r -> {
-                                    Comment.Range range = new Comment.Range();
-                                    range.startLine = r.startLine;
-                                    range.startCharacter = r.startCharacter;
-                                    range.endLine = r.endLine;
-                                    range.endCharacter = r.endCharacter;
-                                    filenameComment.range = range;
-                                });
-                filenameComment.inReplyTo = reviewBatch.getId();
-                unresolved = !config.getInlineCommentsAsResolved();
-            }
-            else {
-                unresolved = !config.getPatchSetCommentsAsResolved();
-            }
-            filenameComment.unresolved = unresolved;
-            filenameComments.add(filenameComment);
-            comments.putIfAbsent(filename, filenameComments);
-        }
-        return comments;
+  private Map<String, List<CommentInput>> getReviewComments(List<ReviewBatch> reviewBatches) {
+    Map<String, List<CommentInput>> comments = new HashMap<>();
+    for (ReviewBatch reviewBatch : reviewBatches) {
+      String message = sanitizeAIChatMessage(reviewBatch.getContent());
+      if (message.trim().isEmpty()) {
+        log.info("Empty message from review not submitted.");
+        continue;
+      }
+      boolean unresolved;
+      String filename = reviewBatch.getFilename();
+      List<CommentInput> filenameComments = comments.getOrDefault(filename, new ArrayList<>());
+      CommentInput filenameComment = new CommentInput();
+      filenameComment.message = message;
+      if (reviewBatch.getLine() != null || reviewBatch.getRange() != null) {
+        filenameComment.line = reviewBatch.getLine();
+        Optional.ofNullable(reviewBatch.getRange())
+            .ifPresent(
+                r -> {
+                  Comment.Range range = new Comment.Range();
+                  range.startLine = r.startLine;
+                  range.startCharacter = r.startCharacter;
+                  range.endLine = r.endLine;
+                  range.endCharacter = r.endCharacter;
+                  filenameComment.range = range;
+                });
+        filenameComment.inReplyTo = reviewBatch.getId();
+        unresolved = !config.getInlineCommentsAsResolved();
+      } else {
+        unresolved = !config.getPatchSetCommentsAsResolved();
+      }
+      filenameComment.unresolved = unresolved;
+      filenameComments.add(filenameComment);
+      comments.putIfAbsent(filename, filenameComments);
     }
+    return comments;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatClient.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatClient.java
index d4540cd..9a62d6c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatClient.java
@@ -1,102 +1,121 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.*;
-
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseContent;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseMessage;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseStreamed;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseUnstreamed;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatToolCall;
 import java.io.BufferedReader;
 import java.io.StringReader;
 import java.util.List;
 import java.util.Optional;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
-abstract public class AIChatClient extends ClientBase {
-    protected boolean isCommentEvent = false;
-    @Getter
-    protected String requestBody;
+public abstract class AIChatClient extends ClientBase {
+  protected boolean isCommentEvent = false;
+  @Getter protected String requestBody;
 
-    public AIChatClient(Configuration config) {
-        super(config);
-    }
+  public AIChatClient(Configuration config) {
+    super(config);
+  }
 
-    protected AIChatResponseContent extractContent(Configuration config, String body) throws Exception {
-        if (config.getAIStreamOutput() && !isCommentEvent) {
-            StringBuilder finalContent = new StringBuilder();
-            try (BufferedReader reader = new BufferedReader(new StringReader(body))) {
-                String line;
-                while ((line = reader.readLine()) != null) {
-                    extractContentFromLine(line).ifPresent(finalContent::append);
-                }
-            }
-            return convertResponseContentFromJson(finalContent.toString());
+  protected AIChatResponseContent extractContent(Configuration config, String body)
+      throws Exception {
+    if (config.getAIStreamOutput() && !isCommentEvent) {
+      StringBuilder finalContent = new StringBuilder();
+      try (BufferedReader reader = new BufferedReader(new StringReader(body))) {
+        String line;
+        while ((line = reader.readLine()) != null) {
+          extractContentFromLine(line).ifPresent(finalContent::append);
         }
-        else {
-            AIChatResponseUnstreamed AIChatResponseUnstreamed =
-                    getGson().fromJson(body, AIChatResponseUnstreamed.class);
-            return getResponseContent(AIChatResponseUnstreamed.getChoices().get(0).getMessage().getToolCalls());
-        }
+      }
+      return convertResponseContentFromJson(finalContent.toString());
+    } else {
+      AIChatResponseUnstreamed AIChatResponseUnstreamed =
+          getGson().fromJson(body, AIChatResponseUnstreamed.class);
+      return getResponseContent(
+          AIChatResponseUnstreamed.getChoices().get(0).getMessage().getToolCalls());
     }
+  }
 
-    protected boolean validateResponse(AIChatResponseContent AIChatResponseContent, String changeId, int attemptInd) {
-        String returnedChangeId = AIChatResponseContent.getChangeId();
-        // A response is considered valid if either no changeId is returned or the changeId returned matches the one
-        // provided in the request
-        boolean isValidated = returnedChangeId == null || changeId.equals(returnedChangeId);
-        if (!isValidated) {
-            log.error("ChangedId mismatch error (attempt #{}).\nExpected value: {}\nReturned value: {}", attemptInd,
-                    changeId, returnedChangeId);
-        }
-        return isValidated;
+  protected boolean validateResponse(
+      AIChatResponseContent AIChatResponseContent, String changeId, int attemptInd) {
+    String returnedChangeId = AIChatResponseContent.getChangeId();
+    // A response is considered valid if either no changeId is returned or the changeId returned
+    // matches the one
+    // provided in the request
+    boolean isValidated = returnedChangeId == null || changeId.equals(returnedChangeId);
+    if (!isValidated) {
+      log.error(
+          "ChangedId mismatch error (attempt #{}).\nExpected value: {}\nReturned value: {}",
+          attemptInd,
+          changeId,
+          returnedChangeId);
     }
+    return isValidated;
+  }
 
-    protected AIChatResponseContent getResponseContent(List<AIChatToolCall> toolCalls) {
-        if (toolCalls.size() > 1) {
-            return mergeToolCalls(toolCalls);
-        } else {
-            return getArgumentAsResponse(toolCalls, 0);
-        }
+  protected AIChatResponseContent getResponseContent(List<AIChatToolCall> toolCalls) {
+    if (toolCalls.size() > 1) {
+      return mergeToolCalls(toolCalls);
+    } else {
+      return getArgumentAsResponse(toolCalls, 0);
     }
+  }
 
-    protected Optional<String> extractContentFromLine(String line) {
-        String dataPrefix = "data: {\"id\"";
+  protected Optional<String> extractContentFromLine(String line) {
+    String dataPrefix = "data: {\"id\"";
 
-        if (!line.startsWith(dataPrefix)) {
-            return Optional.empty();
-        }
-        AIChatResponseStreamed AIChatResponseStreamed =
-                getGson().fromJson(line.substring("data: ".length()), AIChatResponseStreamed.class);
-        AIChatResponseMessage delta = AIChatResponseStreamed.getChoices().get(0).getDelta();
-        if (delta == null || delta.getToolCalls() == null) {
-            return Optional.empty();
-        }
-        String content = getArgumentAsString(delta.getToolCalls(), 0);
-        return Optional.ofNullable(content);
+    if (!line.startsWith(dataPrefix)) {
+      return Optional.empty();
     }
-
-    private AIChatResponseContent convertResponseContentFromJson(String content) {
-        return getGson().fromJson(content, AIChatResponseContent.class);
+    AIChatResponseStreamed AIChatResponseStreamed =
+        getGson().fromJson(line.substring("data: ".length()), AIChatResponseStreamed.class);
+    AIChatResponseMessage delta = AIChatResponseStreamed.getChoices().get(0).getDelta();
+    if (delta == null || delta.getToolCalls() == null) {
+      return Optional.empty();
     }
+    String content = getArgumentAsString(delta.getToolCalls(), 0);
+    return Optional.ofNullable(content);
+  }
 
-    private String getArgumentAsString(List<AIChatToolCall> toolCalls, int ind) {
-        return toolCalls.get(ind).getFunction().getArguments();
-    }
+  private AIChatResponseContent convertResponseContentFromJson(String content) {
+    return getGson().fromJson(content, AIChatResponseContent.class);
+  }
 
-    private AIChatResponseContent getArgumentAsResponse(List<AIChatToolCall> toolCalls, int ind) {
-        return convertResponseContentFromJson(getArgumentAsString(toolCalls, ind));
-    }
+  private String getArgumentAsString(List<AIChatToolCall> toolCalls, int ind) {
+    return toolCalls.get(ind).getFunction().getArguments();
+  }
 
-    private AIChatResponseContent mergeToolCalls(List<AIChatToolCall> toolCalls) {
-        AIChatResponseContent responseContent = getArgumentAsResponse(toolCalls, 0);
-        for (int ind = 1; ind < toolCalls.size(); ind++) {
-            responseContent.getReplies().addAll(
-                    getArgumentAsResponse(toolCalls, ind).getReplies()
-            );
-        }
-        return responseContent;
+  private AIChatResponseContent getArgumentAsResponse(List<AIChatToolCall> toolCalls, int ind) {
+    return convertResponseContentFromJson(getArgumentAsString(toolCalls, ind));
+  }
+
+  private AIChatResponseContent mergeToolCalls(List<AIChatToolCall> toolCalls) {
+    AIChatResponseContent responseContent = getArgumentAsResponse(toolCalls, 0);
+    for (int ind = 1; ind < toolCalls.size(); ind++) {
+      responseContent.getReplies().addAll(getArgumentAsResponse(toolCalls, ind).getReplies());
     }
+    return responseContent;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatParameters.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatParameters.java
index 1f4ad44..7ee587d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatParameters.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatParameters.java
@@ -1,38 +1,52 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
-
 import java.util.concurrent.ThreadLocalRandom;
 
 public class AIChatParameters extends ClientBase {
-    private static boolean isCommentEvent;
+  private static boolean isCommentEvent;
 
-    public AIChatParameters(Configuration config, boolean isCommentEvent) {
-        super(config);
-        AIChatParameters.isCommentEvent = isCommentEvent;
-    }
+  public AIChatParameters(Configuration config, boolean isCommentEvent) {
+    super(config);
+    AIChatParameters.isCommentEvent = isCommentEvent;
+  }
 
-    public double getGptTemperature() {
-        if (isCommentEvent) {
-            return retrieveTemperature(Configuration.KEY_AI_COMMENT_TEMPERATURE,
-                    Configuration.DEFAULT_AI_CHAT_COMMENT_TEMPERATURE);
-        }
-       else {
-            return retrieveTemperature(Configuration.KEY_AI_REVIEW_TEMPERATURE,
-                    Configuration.DEFAULT_AI_CHAT_REVIEW_TEMPERATURE);
-        }
+  public double getGptTemperature() {
+    if (isCommentEvent) {
+      return retrieveTemperature(
+          Configuration.KEY_AI_COMMENT_TEMPERATURE,
+          Configuration.DEFAULT_AI_CHAT_COMMENT_TEMPERATURE);
+    } else {
+      return retrieveTemperature(
+          Configuration.KEY_AI_REVIEW_TEMPERATURE,
+          Configuration.DEFAULT_AI_CHAT_REVIEW_TEMPERATURE);
     }
+  }
 
-    public boolean getStreamOutput() {
-        return config.getAIStreamOutput() && !isCommentEvent;
-    }
+  public boolean getStreamOutput() {
+    return config.getAIStreamOutput() && !isCommentEvent;
+  }
 
-    public int getRandomSeed() {
-        return ThreadLocalRandom.current().nextInt();
-    }
+  public int getRandomSeed() {
+    return ThreadLocalRandom.current().nextInt();
+  }
 
-    private Double retrieveTemperature(String temperatureKey, Double defaultTemperature) {
-        return Double.parseDouble(config.getString(temperatureKey, String.valueOf(defaultTemperature)));
-    }
+  private Double retrieveTemperature(String temperatureKey, Double defaultTemperature) {
+    return Double.parseDouble(config.getString(temperatureKey, String.valueOf(defaultTemperature)));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatTools.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatTools.java
index 6a74adb..ffc3f07 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatTools.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/api/openai/AIChatTools.java
@@ -1,32 +1,47 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatTool;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatToolChoice;
 import com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils;
-
 import java.io.IOException;
 import java.io.InputStreamReader;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-
 public class AIChatTools {
-    public static AIChatTool retrieveFormatRepliesTool() {
-        AIChatTool tools;
-        try (InputStreamReader reader = FileUtils.getInputStreamReader("config/formatRepliesTool.json")) {
-            tools = getGson().fromJson(reader, AIChatTool.class);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to load data for ChatGPT `format_replies` tool", e);
-        }
-        return tools;
+  public static AIChatTool retrieveFormatRepliesTool() {
+    AIChatTool tools;
+    try (InputStreamReader reader =
+        FileUtils.getInputStreamReader("config/formatRepliesTool.json")) {
+      tools = getGson().fromJson(reader, AIChatTool.class);
+    } catch (IOException e) {
+      throw new RuntimeException("Failed to load data for ChatGPT `format_replies` tool", e);
     }
+    return tools;
+  }
 
-    public static AIChatToolChoice retrieveFormatRepliesToolChoice() {
-        AIChatToolChoice toolChoice;
-        try (InputStreamReader reader = FileUtils.getInputStreamReader("config/formatRepliesToolChoice.json")) {
-            toolChoice = getGson().fromJson(reader, AIChatToolChoice.class);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to load data for ChatGPT `format_replies` tool choice", e);
-        }
-        return toolChoice;
+  public static AIChatToolChoice retrieveFormatRepliesToolChoice() {
+    AIChatToolChoice toolChoice;
+    try (InputStreamReader reader =
+        FileUtils.getInputStreamReader("config/formatRepliesToolChoice.json")) {
+      toolChoice = getGson().fromJson(reader, AIChatToolChoice.class);
+    } catch (IOException e) {
+      throw new RuntimeException("Failed to load data for ChatGPT `format_replies` tool choice", e);
     }
+    return toolChoice;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/commands/ClientCommands.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/commands/ClientCommands.java
index 11b6e89..a37a733 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/commands/ClientCommands.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/commands/ClientCommands.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.commands;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -7,190 +21,193 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.Directives;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 @Getter
 public class ClientCommands extends ClientBase {
-    private enum CommandSet {
-        REVIEW,
-        REVIEW_LAST,
-        DIRECTIVE,
-        CONFIGURE
+  private enum CommandSet {
+    REVIEW,
+    REVIEW_LAST,
+    DIRECTIVE,
+    CONFIGURE
+  }
+
+  private enum ReviewOptionSet {
+    FILTER,
+    DEBUG
+  }
+
+  private enum ConfigureOptionSet {
+    RESET
+  }
+
+  private static final Map<String, CommandSet> COMMAND_MAP =
+      Map.of(
+          "review", CommandSet.REVIEW,
+          "review_last", CommandSet.REVIEW_LAST,
+          "directive", CommandSet.DIRECTIVE,
+          "configure", CommandSet.CONFIGURE);
+  private static final Map<String, ReviewOptionSet> REVIEW_OPTION_MAP =
+      Map.of(
+          "filter", ReviewOptionSet.FILTER,
+          "debug", ReviewOptionSet.DEBUG);
+  private static final List<CommandSet> REVIEW_COMMANDS =
+      new ArrayList<>(List.of(CommandSet.REVIEW, CommandSet.REVIEW_LAST));
+  private static final List<CommandSet> HISTORY_COMMANDS =
+      new ArrayList<>(List.of(CommandSet.DIRECTIVE));
+  private static final Map<String, ConfigureOptionSet> CONFIGURE_OPTION_MAP =
+      Map.of("reset", ConfigureOptionSet.RESET);
+  // Option values can be either a sequence of chars enclosed in double quotes or a sequence of
+  // non-space chars.
+  private static final String OPTION_VALUES = "\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|\\S+";
+  private static final Pattern COMMAND_PATTERN =
+      Pattern.compile(
+          "/("
+              + String.join("|", COMMAND_MAP.keySet())
+              + ")\\b((?:\\s+--\\w+(?:=(?:"
+              + OPTION_VALUES
+              + "))?)+)?");
+  private static final Pattern OPTIONS_PATTERN =
+      Pattern.compile("--(\\w+)(?:=(" + OPTION_VALUES + "))?");
+
+  private final ChangeSetData changeSetData;
+  private final Directives directives;
+  private final Localizer localizer;
+
+  private DynamicConfiguration dynamicConfiguration;
+  private boolean containingHistoryCommand;
+  private boolean modifiedDynamicConfig;
+  private boolean shouldResetDynamicConfig;
+
+  public ClientCommands(
+      Configuration config,
+      ChangeSetData changeSetData,
+      PluginDataHandlerProvider pluginDataHandlerProvider,
+      Localizer localizer) {
+    super(config);
+    this.localizer = localizer;
+    this.changeSetData = changeSetData;
+    directives = new Directives(changeSetData);
+    // The `dynamicConfiguration` instance is utilized only for parsing current client messages, not
+    // the history
+    if (pluginDataHandlerProvider != null) {
+      dynamicConfiguration = new DynamicConfiguration(pluginDataHandlerProvider);
     }
-    private enum ReviewOptionSet {
-        FILTER,
-        DEBUG
+    containingHistoryCommand = false;
+    modifiedDynamicConfig = false;
+    shouldResetDynamicConfig = false;
+  }
+
+  public boolean parseCommands(String comment, boolean isNotHistory) {
+    boolean commandFound = false;
+    Matcher reviewCommandMatcher = COMMAND_PATTERN.matcher(comment);
+    while (reviewCommandMatcher.find()) {
+      CommandSet command = COMMAND_MAP.get(reviewCommandMatcher.group(1));
+      parseOptions(command, reviewCommandMatcher, isNotHistory);
+      parseCommand(command, comment, isNotHistory);
+      commandFound = true;
     }
-    private enum ConfigureOptionSet {
-        RESET
+    return commandFound;
+  }
+
+  public String parseRemoveCommands(String comment) {
+    if (parseCommands(comment, false)) {
+      return removeCommands(comment);
     }
+    return comment;
+  }
 
-    private static final Map<String, CommandSet> COMMAND_MAP = Map.of(
-            "review", CommandSet.REVIEW,
-            "review_last", CommandSet.REVIEW_LAST,
-            "directive", CommandSet.DIRECTIVE,
-            "configure", CommandSet.CONFIGURE
-    );
-    private static final Map<String, ReviewOptionSet> REVIEW_OPTION_MAP = Map.of(
-            "filter", ReviewOptionSet.FILTER,
-            "debug", ReviewOptionSet.DEBUG
-    );
-    private static final List<CommandSet> REVIEW_COMMANDS = new ArrayList<>(List.of(
-            CommandSet.REVIEW,
-            CommandSet.REVIEW_LAST
-    ));
-    private static final List<CommandSet> HISTORY_COMMANDS = new ArrayList<>(List.of(
-            CommandSet.DIRECTIVE
-    ));
-    private static final Map<String, ConfigureOptionSet> CONFIGURE_OPTION_MAP = Map.of(
-            "reset", ConfigureOptionSet.RESET
-    );
-    // Option values can be either a sequence of chars enclosed in double quotes or a sequence of non-space chars.
-    private static final String OPTION_VALUES = "\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|\\S+";
-    private static final Pattern COMMAND_PATTERN = Pattern.compile("/(" + String.join("|",
-            COMMAND_MAP.keySet()) + ")\\b((?:\\s+--\\w+(?:=(?:" + OPTION_VALUES + "))?)+)?");
-    private static final Pattern OPTIONS_PATTERN = Pattern.compile("--(\\w+)(?:=(" + OPTION_VALUES + "))?");
+  private String removeCommands(String comment) {
+    Matcher reviewCommandMatcher = COMMAND_PATTERN.matcher(comment);
+    return reviewCommandMatcher.replaceAll("");
+  }
 
-    private final ChangeSetData changeSetData;
-    private final Directives directives;
-    private final Localizer localizer;
-
-    private DynamicConfiguration dynamicConfiguration;
-    private boolean containingHistoryCommand;
-    private boolean modifiedDynamicConfig;
-    private boolean shouldResetDynamicConfig;
-
-    public ClientCommands(
-            Configuration config,
-            ChangeSetData changeSetData,
-            PluginDataHandlerProvider pluginDataHandlerProvider,
-            Localizer localizer
-    ) {
-        super(config);
-        this.localizer = localizer;
-        this.changeSetData = changeSetData;
-        directives = new Directives(changeSetData);
-        // The `dynamicConfiguration` instance is utilized only for parsing current client messages, not the history
-        if (pluginDataHandlerProvider != null) {
-            dynamicConfiguration = new DynamicConfiguration(pluginDataHandlerProvider);
+  private void parseCommand(CommandSet command, String comment, boolean isNotHistory) {
+    if (isNotHistory) {
+      if (REVIEW_COMMANDS.contains(command)) {
+        changeSetData.setForcedReview(true);
+        if (command == CommandSet.REVIEW_LAST) {
+          log.info("Forced review command applied to the last Patch Set");
+          changeSetData.setForcedReviewLastPatchSet(true);
+        } else {
+          log.info("Forced review command applied to the entire Change Set");
         }
-        containingHistoryCommand = false;
-        modifiedDynamicConfig = false;
-        shouldResetDynamicConfig = false;
-    }
-
-    public boolean parseCommands(String comment, boolean isNotHistory) {
-        boolean commandFound = false;
-        Matcher reviewCommandMatcher = COMMAND_PATTERN.matcher(comment);
-        while (reviewCommandMatcher.find()) {
-            CommandSet command = COMMAND_MAP.get(reviewCommandMatcher.group(1));
-            parseOptions(command, reviewCommandMatcher, isNotHistory);
-            parseCommand(command, comment, isNotHistory);
-            commandFound = true;
+      } else if (command == CommandSet.CONFIGURE) {
+        if (config.getEnableMessageDebugging()) {
+          changeSetData.setHideAICodeReview(true);
+          dynamicConfiguration.updateConfiguration(modifiedDynamicConfig, shouldResetDynamicConfig);
+        } else {
+          changeSetData.setReviewSystemMessage(
+              localizer.getText("message.configure.from.messages.disabled"));
+          log.debug(
+              "Unable to change configuration from messages: `enableMessageDebugging` config must"
+                  + "be set to true");
         }
-        return commandFound;
+      }
     }
+    if (HISTORY_COMMANDS.contains(command)) {
+      containingHistoryCommand = true;
+      if (command == CommandSet.DIRECTIVE) {
+        directives.addDirective(removeCommands(comment));
+      }
+    }
+  }
 
-    public String parseRemoveCommands(String comment) {
-        if (parseCommands(comment, false)) {
-            return removeCommands(comment);
-        }
-        return comment;
+  private void parseOptions(
+      CommandSet command, Matcher reviewCommandMatcher, boolean isNotHistory) {
+    // Command options need to be parsed only when processing the current message, not the message
+    // history
+    if (reviewCommandMatcher.group(2) == null || !isNotHistory) return;
+    Matcher reviewOptionsMatcher = OPTIONS_PATTERN.matcher(reviewCommandMatcher.group(2));
+    while (reviewOptionsMatcher.find()) {
+      parseSingleOption(command, reviewOptionsMatcher);
     }
+  }
 
-    private String removeCommands(String comment) {
-        Matcher reviewCommandMatcher = COMMAND_PATTERN.matcher(comment);
-        return reviewCommandMatcher.replaceAll("");
+  private void parseSingleOption(CommandSet command, Matcher reviewOptionsMatcher) {
+    String optionKey = reviewOptionsMatcher.group(1);
+    String optionValue =
+        Optional.ofNullable(reviewOptionsMatcher.group(2))
+            .map(val -> val.replaceAll("^\"(.*)\"$", "$1"))
+            .orElse("");
+    if (REVIEW_COMMANDS.contains(command)) {
+      switch (REVIEW_OPTION_MAP.get(optionKey)) {
+        case FILTER:
+          boolean value = Boolean.parseBoolean(optionValue);
+          log.debug("Option 'replyFilterEnabled' set to {}", value);
+          changeSetData.setReplyFilterEnabled(value);
+          break;
+        case DEBUG:
+          if (config.getEnableMessageDebugging()) {
+            log.debug("Response Mode set to Debug");
+            changeSetData.setDebugReviewMode(true);
+            changeSetData.setReplyFilterEnabled(false);
+          } else {
+            changeSetData.setReviewSystemMessage(
+                localizer.getText("message.debugging.review.disabled"));
+            log.debug(
+                "Unable to set Response Mode to Debug: `enableMessageDebugging` config "
+                    + "must be set to true");
+          }
+          break;
+      }
+    } else if (command == CommandSet.CONFIGURE && config.getEnableMessageDebugging()) {
+      if (CONFIGURE_OPTION_MAP.get(optionKey) == ConfigureOptionSet.RESET) {
+        shouldResetDynamicConfig = true;
+        log.debug("Resetting configuration settings");
+      } else {
+        modifiedDynamicConfig = true;
+        log.debug("Updating configuration setting '{}' to '{}'", optionKey, optionValue);
+        dynamicConfiguration.setConfig(optionKey, optionValue);
+      }
     }
-
-    private void parseCommand(CommandSet command, String comment, boolean isNotHistory) {
-        if (isNotHistory) {
-            if (REVIEW_COMMANDS.contains(command)) {
-                changeSetData.setForcedReview(true);
-                if (command == CommandSet.REVIEW_LAST) {
-                    log.info("Forced review command applied to the last Patch Set");
-                    changeSetData.setForcedReviewLastPatchSet(true);
-                }
-                else {
-                    log.info("Forced review command applied to the entire Change Set");
-                }
-            }
-            else if (command == CommandSet.CONFIGURE) {
-                if (config.getEnableMessageDebugging()) {
-                    changeSetData.setHideAICodeReview(true);
-                    dynamicConfiguration.updateConfiguration(modifiedDynamicConfig, shouldResetDynamicConfig);
-                }
-                else {
-                    changeSetData.setReviewSystemMessage(localizer.getText(
-                            "message.configure.from.messages.disabled"
-                    ));
-                    log.debug("Unable to change configuration from messages: `enableMessageDebugging` config must" +
-                            "be set to true");
-                }
-            }
-        }
-        if (HISTORY_COMMANDS.contains(command)) {
-            containingHistoryCommand = true;
-            if (command == CommandSet.DIRECTIVE) {
-                directives.addDirective(removeCommands(comment));
-            }
-        }
-    }
-
-    private void parseOptions(CommandSet command, Matcher reviewCommandMatcher, boolean isNotHistory) {
-        // Command options need to be parsed only when processing the current message, not the message history
-        if (reviewCommandMatcher.group(2) == null || !isNotHistory) return;
-        Matcher reviewOptionsMatcher = OPTIONS_PATTERN.matcher(reviewCommandMatcher.group(2));
-        while (reviewOptionsMatcher.find()) {
-            parseSingleOption(command, reviewOptionsMatcher);
-        }
-    }
-
-    private void parseSingleOption(CommandSet command, Matcher reviewOptionsMatcher) {
-        String optionKey = reviewOptionsMatcher.group(1);
-        String optionValue = Optional.ofNullable(reviewOptionsMatcher.group(2))
-                .map(val -> val.replaceAll("^\"(.*)\"$", "$1"))
-                .orElse("");
-        if (REVIEW_COMMANDS.contains(command)) {
-            switch (REVIEW_OPTION_MAP.get(optionKey)) {
-                case FILTER:
-                    boolean value = Boolean.parseBoolean(optionValue);
-                    log.debug("Option 'replyFilterEnabled' set to {}", value);
-                    changeSetData.setReplyFilterEnabled(value);
-                    break;
-                case DEBUG:
-                    if (config.getEnableMessageDebugging()) {
-                        log.debug("Response Mode set to Debug");
-                        changeSetData.setDebugReviewMode(true);
-                        changeSetData.setReplyFilterEnabled(false);
-                    } else {
-                        changeSetData.setReviewSystemMessage(localizer.getText(
-                                "message.debugging.review.disabled"
-                        ));
-                        log.debug("Unable to set Response Mode to Debug: `enableMessageDebugging` config " +
-                                "must be set to true");
-                    }
-                    break;
-            }
-        } else if (command == CommandSet.CONFIGURE && config.getEnableMessageDebugging()) {
-            if (CONFIGURE_OPTION_MAP.get(optionKey) == ConfigureOptionSet.RESET) {
-                shouldResetDynamicConfig = true;
-                log.debug("Resetting configuration settings");
-            }
-            else {
-                modifiedDynamicConfig = true;
-                log.debug("Updating configuration setting '{}' to '{}'", optionKey, optionValue);
-                dynamicConfiguration.setConfig(optionKey, optionValue);
-            }
-        }
-    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClient.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClient.java
index 6d87ae8..06d1588 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClient.java
@@ -1,70 +1,91 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.http;
 
-import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.*;
-import org.apache.http.NameValuePair;
-
-import java.io.IOException;
-import java.util.Map;
-
 import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
 
+import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
+import java.io.IOException;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import org.apache.http.NameValuePair;
+
 @Slf4j
 public class HttpClient {
-    private final OkHttpClient client = new OkHttpClient();
+  private final OkHttpClient client = new OkHttpClient();
 
-    public String execute(Request request) {
-        try (Response response = client.newCall(request).execute()) {
-            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
-            log.debug("HttpClient Response body: {}", response.body());
-            if (response.body() != null) {
-                return response.body().string();
-            }
-            else {
-                log.error("Request {} returned an empty string", request);
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        return null;
+  public String execute(Request request) {
+    try (Response response = client.newCall(request).execute()) {
+      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
+      log.debug("HttpClient Response body: {}", response.body());
+      if (response.body() != null) {
+        return response.body().string();
+      } else {
+        log.error("Request {} returned an empty string", request);
+      }
+    } catch (IOException e) {
+      throw new RuntimeException(e);
     }
+    return null;
+  }
 
-    public Request createRequest(String uri, Configuration configuration, RequestBody body, Map<String, String> additionalHeaders) {
-        // If body is null, a GET request is initiated. Otherwise, a POST request is sent with the specified body.
-        Request.Builder builder = new Request.Builder()
-                .url(uri);
+  public Request createRequest(
+      String uri,
+      Configuration configuration,
+      RequestBody body,
+      Map<String, String> additionalHeaders) {
+    // If body is null, a GET request is initiated. Otherwise, a POST request is sent with the
+    // specified body.
+    Request.Builder builder = new Request.Builder().url(uri);
 
-        // depending on the aiType, add appropriate authorization header ( if required ).
-        NameValuePair authHeader = configuration.getAuthorizationHeaderInfo();
-        if (authHeader != null) {
-            builder.header(authHeader.getName(), authHeader.getValue());
-        }
-        if (body != null) {
-            builder.post(body);
-        }
-        else {
-            builder.get();
-        }
-        if (additionalHeaders != null) {
-            for (Map.Entry<String, String> header : additionalHeaders.entrySet()) {
-                builder.header(header.getKey(), header.getValue());
-            }
-        }
-        return builder.build();
+    // depending on the aiType, add appropriate authorization header ( if required ).
+    NameValuePair authHeader = configuration.getAuthorizationHeaderInfo();
+    if (authHeader != null) {
+      builder.header(authHeader.getName(), authHeader.getValue());
     }
-
-    public Request createRequestFromJson(String uri, Configuration configuration, Object requestObject,
-                                         Map<String, String> additionalHeaders) {
-        if (requestObject != null) {
-            String bodyJson = getGson().toJson(requestObject);
-            log.debug("Request body: {}", bodyJson);
-            RequestBody body = RequestBody.create(bodyJson, MediaType.get("application/json"));
-
-            return createRequest(uri, configuration, body, additionalHeaders);
-        }
-        else {
-            return createRequest(uri, configuration, null, additionalHeaders);
-        }
+    if (body != null) {
+      builder.post(body);
+    } else {
+      builder.get();
     }
+    if (additionalHeaders != null) {
+      for (Map.Entry<String, String> header : additionalHeaders.entrySet()) {
+        builder.header(header.getKey(), header.getValue());
+      }
+    }
+    return builder.build();
+  }
+
+  public Request createRequestFromJson(
+      String uri,
+      Configuration configuration,
+      Object requestObject,
+      Map<String, String> additionalHeaders) {
+    if (requestObject != null) {
+      String bodyJson = getGson().toJson(requestObject);
+      log.debug("Request body: {}", bodyJson);
+      RequestBody body = RequestBody.create(bodyJson, MediaType.get("application/json"));
+
+      return createRequest(uri, configuration, body, additionalHeaders);
+    } else {
+      return createRequest(uri, configuration, null, additionalHeaders);
+    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClientWithRetry.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClientWithRetry.java
index af6334d..9bed490 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClientWithRetry.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/http/HttpClientWithRetry.java
@@ -1,56 +1,79 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.http;
 
-import com.github.rholder.retry.*;
-import com.google.inject.Singleton;
-import lombok.extern.slf4j.Slf4j;
+import static java.net.HttpURLConnection.HTTP_OK;
 
+import com.github.rholder.retry.Attempt;
+import com.github.rholder.retry.RetryException;
+import com.github.rholder.retry.RetryListener;
+import com.github.rholder.retry.Retryer;
+import com.github.rholder.retry.RetryerBuilder;
+import com.github.rholder.retry.StopStrategies;
+import com.github.rholder.retry.WaitStrategies;
+import com.google.inject.Singleton;
 import java.net.http.HttpClient;
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
 import java.time.Duration;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
-
-import static java.net.HttpURLConnection.HTTP_OK;
+import lombok.extern.slf4j.Slf4j;
 
 @Singleton
 @Slf4j
 public class HttpClientWithRetry {
-    private final Retryer<HttpResponse<String>> retryer;
+  private final Retryer<HttpResponse<String>> retryer;
 
-    private final HttpClient httpClient = HttpClient.newBuilder()
-            .connectTimeout(Duration.ofMinutes(5))
-            .build();
+  private final HttpClient httpClient =
+      HttpClient.newBuilder().connectTimeout(Duration.ofMinutes(5)).build();
 
-    public HttpClientWithRetry() {
-        //Attention, 'com.github.rholder.retry.RetryListener' is marked unstable with @Beta annotation
-        RetryListener listener = new RetryListener() {
-            @Override
-            public <V> void onRetry(Attempt<V> attempt) {
-                if (attempt.hasException()) {
-                    log.error("Retry failed with exception: " + attempt.getExceptionCause());
-                }
+  public HttpClientWithRetry() {
+    // Attention, 'com.github.rholder.retry.RetryListener' is marked unstable with @Beta annotation
+    RetryListener listener =
+        new RetryListener() {
+          @Override
+          public <V> void onRetry(Attempt<V> attempt) {
+            if (attempt.hasException()) {
+              log.error("Retry failed with exception: " + attempt.getExceptionCause());
             }
+          }
         };
 
-        this.retryer = RetryerBuilder.<HttpResponse<String>>newBuilder()
-                .retryIfException()
-                .retryIfResult(response -> {
-                    if (response.statusCode() != HTTP_OK) {
-                        log.error("Retry because HTTP status code is not 200. The status code is: " + response.statusCode());
-                        return true;
-                    } else {
-                        return false;
-                    }
-                }).withWaitStrategy(WaitStrategies.fixedWait(20, TimeUnit.SECONDS))
-                .withStopStrategy(StopStrategies.stopAfterAttempt(5))
-                .withRetryListener(listener)
-                .build();
-    }
+    this.retryer =
+        RetryerBuilder.<HttpResponse<String>>newBuilder()
+            .retryIfException()
+            .retryIfResult(
+                response -> {
+                  if (response.statusCode() != HTTP_OK) {
+                    log.error(
+                        "Retry because HTTP status code is not 200. The status code is: "
+                            + response.statusCode());
+                    return true;
+                  } else {
+                    return false;
+                  }
+                })
+            .withWaitStrategy(WaitStrategies.fixedWait(20, TimeUnit.SECONDS))
+            .withStopStrategy(StopStrategies.stopAfterAttempt(5))
+            .withRetryListener(listener)
+            .build();
+  }
 
-    public HttpResponse<String> execute(HttpRequest request) throws ExecutionException, RetryException {
-        return retryer.call(() -> httpClient.send(request, HttpResponse.BodyHandlers.ofString()));
-    }
-
+  public HttpResponse<String> execute(HttpRequest request)
+      throws ExecutionException, RetryException {
+    return retryer.call(() -> httpClient.send(request, HttpResponse.BodyHandlers.ofString()));
+  }
 }
-
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/ClientMessage.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/ClientMessage.java
index 689c64d..47d3dcb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/ClientMessage.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/ClientMessage.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.messages;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -6,103 +20,104 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.commands.ClientCommands;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 @Slf4j
 public class ClientMessage extends ClientBase {
-    private static final Pattern MESSAGE_HEADING_PATTERN = Pattern.compile(
-            "^(?:Patch Set \\d+:[^\\n]*\\s+(?:\\(\\d+ comments?\\)\\s*)?)+");
+  private static final Pattern MESSAGE_HEADING_PATTERN =
+      Pattern.compile("^(?:Patch Set \\d+:[^\\n]*\\s+(?:\\(\\d+ comments?\\)\\s*)?)+");
 
-    private final Pattern botMentionPattern;
-    private final ClientCommands clientCommands;
-    private final DebugCodeBlocksReview debugCodeBlocksReview;
-    private final DebugCodeBlocksDynamicSettings debugCodeBlocksDynamicSettings;
+  private final Pattern botMentionPattern;
+  private final ClientCommands clientCommands;
+  private final DebugCodeBlocksReview debugCodeBlocksReview;
+  private final DebugCodeBlocksDynamicSettings debugCodeBlocksDynamicSettings;
 
-    @Getter
-    private String message;
+  @Getter private String message;
 
-    public ClientMessage(
-            Configuration config,
-            ChangeSetData changeSetData,
-            PluginDataHandlerProvider pluginDataHandlerProvider,
-            Localizer localizer
-    ) {
-        super(config);
-        botMentionPattern = getBotMentionPattern();
-        clientCommands = new ClientCommands(config, changeSetData, pluginDataHandlerProvider, localizer);
-        debugCodeBlocksReview = new DebugCodeBlocksReview(localizer);
-        debugCodeBlocksDynamicSettings = new DebugCodeBlocksDynamicSettings(localizer);
+  public ClientMessage(
+      Configuration config,
+      ChangeSetData changeSetData,
+      PluginDataHandlerProvider pluginDataHandlerProvider,
+      Localizer localizer) {
+    super(config);
+    botMentionPattern = getBotMentionPattern();
+    clientCommands =
+        new ClientCommands(config, changeSetData, pluginDataHandlerProvider, localizer);
+    debugCodeBlocksReview = new DebugCodeBlocksReview(localizer);
+    debugCodeBlocksDynamicSettings = new DebugCodeBlocksDynamicSettings(localizer);
+  }
+
+  public ClientMessage(
+      Configuration config, ChangeSetData changeSetData, String message, Localizer localizer) {
+    this(config, changeSetData, (PluginDataHandlerProvider) null, localizer);
+    this.message = message;
+  }
+
+  public boolean isBotAddressed(String message) {
+    log.debug("Processing comment: {}", message);
+    Matcher userMatcher = botMentionPattern.matcher(message);
+    if (!userMatcher.find()) {
+      log.debug(
+          "Skipping action since the comment does not mention the ChatGPT bot."
+              + " Expected bot name in comment: {}, Actual comment text: {}",
+          config.getGerritUserName(),
+          message);
+      return false;
     }
+    return true;
+  }
 
-    public ClientMessage(Configuration config, ChangeSetData changeSetData, String message, Localizer localizer) {
-        this(config, changeSetData, (PluginDataHandlerProvider) null, localizer);
-        this.message = message;
-    }
+  public ClientMessage removeHeadings() {
+    message = MESSAGE_HEADING_PATTERN.matcher(message).replaceAll("");
+    return this;
+  }
 
-    public boolean isBotAddressed(String message) {
-        log.debug("Processing comment: {}", message);
-        Matcher userMatcher = botMentionPattern.matcher(message);
-        if (!userMatcher.find()) {
-            log.debug("Skipping action since the comment does not mention the ChatGPT bot." +
-                            " Expected bot name in comment: {}, Actual comment text: {}",
-                    config.getGerritUserName(), message);
-            return false;
-        }
-        return true;
-    }
+  public ClientMessage removeMentions() {
+    message = botMentionPattern.matcher(message).replaceAll("").trim();
+    return this;
+  }
 
-    public ClientMessage removeHeadings() {
-        message = MESSAGE_HEADING_PATTERN.matcher(message).replaceAll("");
-        return this;
-    }
+  public ClientMessage parseRemoveCommands() {
+    message = clientCommands.parseRemoveCommands(message);
+    return this;
+  }
 
-    public ClientMessage removeMentions() {
-        message = botMentionPattern.matcher(message).replaceAll("").trim();
-        return this;
-    }
+  public ClientMessage removeDebugCodeBlocksReview() {
+    message = debugCodeBlocksReview.removeDebugCodeBlocks(message);
+    return this;
+  }
 
-    public ClientMessage parseRemoveCommands() {
-        message = clientCommands.parseRemoveCommands(message);
-        return this;
-    }
+  public ClientMessage removeDebugCodeBlocksDynamicSettings() {
+    message = debugCodeBlocksDynamicSettings.removeDebugCodeBlocks(message);
+    return this;
+  }
 
-    public ClientMessage removeDebugCodeBlocksReview() {
-        message = debugCodeBlocksReview.removeDebugCodeBlocks(message);
-        return this;
-    }
+  public boolean isContainingHistoryCommand() {
+    return clientCommands.isContainingHistoryCommand();
+  }
 
-    public ClientMessage removeDebugCodeBlocksDynamicSettings() {
-        message = debugCodeBlocksDynamicSettings.removeDebugCodeBlocks(message);
-        return this;
-    }
+  public boolean parseCommands(String comment, boolean isNotHistory) {
+    return clientCommands.parseCommands(comment, isNotHistory);
+  }
 
-    public boolean isContainingHistoryCommand() {
-        return clientCommands.isContainingHistoryCommand();
-    }
+  public void processHistoryCommand() {
+    clientCommands.getDirectives().copyDirectiveToSettings();
+  }
 
-    public boolean parseCommands(String comment, boolean isNotHistory) {
-        return clientCommands.parseCommands(comment, isNotHistory);
-    }
+  private Pattern getBotMentionPattern() {
+    String emailRegex = "^(?!>).*?(?:@" + getUserNameOrEmail() + ")\\b";
+    return Pattern.compile(emailRegex, Pattern.MULTILINE);
+  }
 
-    public void processHistoryCommand() {
-        clientCommands.getDirectives().copyDirectiveToSettings();
+  private String getUserNameOrEmail() {
+    String escapedUserName = Pattern.quote(config.getGerritUserName());
+    String userEmail = config.getGerritUserEmail();
+    if (userEmail.isBlank()) {
+      return escapedUserName;
     }
-
-    private Pattern getBotMentionPattern() {
-        String emailRegex = "^(?!>).*?(?:@" + getUserNameOrEmail() + ")\\b";
-        return Pattern.compile(emailRegex, Pattern.MULTILINE);
-    }
-
-    private String getUserNameOrEmail() {
-        String escapedUserName = Pattern.quote(config.getGerritUserName());
-        String userEmail = config.getGerritUserEmail();
-        if (userEmail.isBlank()) {
-            return  escapedUserName;
-        }
-        return escapedUserName + "|" + Pattern.quote(userEmail);
-    }
+    return escapedUserName + "|" + Pattern.quote(userEmail);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocks.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocks.java
index 8894a33..fba17cf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocks.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocks.java
@@ -1,32 +1,53 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.messages;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.CODE_DELIMITER;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.CODE_DELIMITER_BEGIN;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
-
 public class DebugCodeBlocks {
-    protected final String commentOpening;
-    protected final Pattern debugMessagePattern;
+  protected final String commentOpening;
+  protected final Pattern debugMessagePattern;
 
-    public DebugCodeBlocks(String openingTitle) {
-        commentOpening = CODE_DELIMITER_BEGIN + openingTitle + "\n";
-        debugMessagePattern = Pattern.compile("\\s+" + CODE_DELIMITER +"\\s*" + openingTitle + ".*" +
-                CODE_DELIMITER + "\\s*", Pattern.DOTALL);
-    }
+  public DebugCodeBlocks(String openingTitle) {
+    commentOpening = CODE_DELIMITER_BEGIN + openingTitle + "\n";
+    debugMessagePattern =
+        Pattern.compile(
+            "\\s+" + CODE_DELIMITER + "\\s*" + openingTitle + ".*" + CODE_DELIMITER + "\\s*",
+            Pattern.DOTALL);
+  }
 
-    public String removeDebugCodeBlocks(String message) {
-        Matcher debugMessagematcher = debugMessagePattern.matcher(message);
-        return debugMessagematcher.replaceAll("");
-    }
+  public String removeDebugCodeBlocks(String message) {
+    Matcher debugMessagematcher = debugMessagePattern.matcher(message);
+    return debugMessagematcher.replaceAll("");
+  }
 
-    protected String getDebugCodeBlock(List<String> panelItems) {
-        return joinWithNewLine(new ArrayList<>() {{
+  protected String getDebugCodeBlock(List<String> panelItems) {
+    return joinWithNewLine(
+        new ArrayList<>() {
+          {
             add(commentOpening);
             addAll(panelItems);
             add(CODE_DELIMITER);
-        }});
-    }
+          }
+        });
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksDynamicSettings.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksDynamicSettings.java
index 0c57376..a234b9f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksDynamicSettings.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksDynamicSettings.java
@@ -1,20 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.messages;
 
-import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
-
-import java.util.List;
-import java.util.Map;
-
 import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.prettyStringifyMap;
 
-public class DebugCodeBlocksDynamicSettings extends DebugCodeBlocks {
-    public DebugCodeBlocksDynamicSettings(Localizer localizer) {
-        super(localizer.getText("message.dynamic.configuration.title"));
-    }
+import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
+import java.util.List;
+import java.util.Map;
 
-    public String getDebugCodeBlock(Map<String, String> dynamicConfig) {
-        return super.getDebugCodeBlock(List.of(
-                prettyStringifyMap(dynamicConfig)
-        ));
-    }
+public class DebugCodeBlocksDynamicSettings extends DebugCodeBlocks {
+  public DebugCodeBlocksDynamicSettings(Localizer localizer) {
+    super(localizer.getText("message.dynamic.configuration.title"));
+  }
+
+  public String getDebugCodeBlock(Map<String, String> dynamicConfig) {
+    return super.getDebugCodeBlock(List.of(prettyStringifyMap(dynamicConfig)));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksReview.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksReview.java
index 2832cb2..1306837 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/messages/DebugCodeBlocksReview.java
@@ -1,23 +1,34 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.messages;
 
-import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
-
-import java.util.List;
-
 import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.prettyStringifyObject;
 
+import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
+import java.util.List;
+
 public class DebugCodeBlocksReview extends DebugCodeBlocks {
-    private static final String HIDDEN_REPLY = "hidden: %s";
+  private static final String HIDDEN_REPLY = "hidden: %s";
 
-    public DebugCodeBlocksReview(Localizer localizer) {
-        super(localizer.getText("message.debugging.review.title"));
-    }
+  public DebugCodeBlocksReview(Localizer localizer) {
+    super(localizer.getText("message.debugging.review.title"));
+  }
 
-    public String getDebugCodeBlock(AIChatReplyItem replyItem, boolean isHidden) {
-        return super.getDebugCodeBlock(List.of(
-                String.format(HIDDEN_REPLY, isHidden),
-                prettyStringifyObject(replyItem)
-        ));
-    }
+  public String getDebugCodeBlock(AIChatReplyItem replyItem, boolean isHidden) {
+    return super.getDebugCodeBlock(
+        List.of(String.format(HIDDEN_REPLY, isHidden), prettyStringifyObject(replyItem)));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/CodeFinder.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/CodeFinder.java
index e02a899..6300ae9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/CodeFinder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/CodeFinder.java
@@ -1,143 +1,176 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.code;
 
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritCodeRange;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.code.CodeFinderDiff;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.diff.DiffContent;
-import lombok.extern.slf4j.Slf4j;
-
 import java.lang.reflect.Field;
 import java.util.List;
 import java.util.TreeMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class CodeFinder {
-    private static final String PUNCTUATION_REGEX = "([()\\[\\]{}<>:;,?&+\\-*/%|=])";
-    private static final String BEGINNING_DIFF_REGEX = "(?:^|\n)[+\\-]";
-    private static final String ENDING_ELLIPSIS_REGEX = "\\.\\.\\.\\W*$";
+  private static final String PUNCTUATION_REGEX = "([()\\[\\]{}<>:;,?&+\\-*/%|=])";
+  private static final String BEGINNING_DIFF_REGEX = "(?:^|\n)[+\\-]";
+  private static final String ENDING_ELLIPSIS_REGEX = "\\.\\.\\.\\W*$";
 
-    private final String NON_PRINTING_REPLACEMENT;
-    private final String PUNCTUATION_REPLACEMENT;
-    private final String PLACEHOLDER_REGEX;
-    private final List<CodeFinderDiff> codeFinderDiffs;
+  private final String NON_PRINTING_REPLACEMENT;
+  private final String PUNCTUATION_REPLACEMENT;
+  private final String PLACEHOLDER_REGEX;
+  private final List<CodeFinderDiff> codeFinderDiffs;
 
-    private int commentedLine;
-    private Pattern commentedCodePattern;
-    private GerritCodeRange currentCodeRange;
-    private GerritCodeRange closestCodeRange;
+  private int commentedLine;
+  private Pattern commentedCodePattern;
+  private GerritCodeRange currentCodeRange;
+  private GerritCodeRange closestCodeRange;
 
-    public CodeFinder(List<CodeFinderDiff> codeFinderDiffs, String randomPlaceholder) {
-        this.codeFinderDiffs = codeFinderDiffs;
-        NON_PRINTING_REPLACEMENT = "\\\\E" + randomPlaceholder +"\\\\Q";
-        PUNCTUATION_REPLACEMENT = "\\\\E" + randomPlaceholder +"\\\\$1" + randomPlaceholder +"\\\\Q";
-        PLACEHOLDER_REGEX = "(?:" + randomPlaceholder + ")+";
-    }
+  public CodeFinder(List<CodeFinderDiff> codeFinderDiffs, String randomPlaceholder) {
+    this.codeFinderDiffs = codeFinderDiffs;
+    NON_PRINTING_REPLACEMENT = "\\\\E" + randomPlaceholder + "\\\\Q";
+    PUNCTUATION_REPLACEMENT = "\\\\E" + randomPlaceholder + "\\\\$1" + randomPlaceholder + "\\\\Q";
+    PLACEHOLDER_REGEX = "(?:" + randomPlaceholder + ")+";
+  }
 
-    public GerritCodeRange findCommentedCode(AIChatReplyItem replyItem, int commentedLine) {
-        this.commentedLine = commentedLine;
-        updateCodePattern(replyItem);
-        currentCodeRange = null;
-        closestCodeRange = null;
-        for (CodeFinderDiff codeFinderDiff : codeFinderDiffs) {
-            for (Field diffField : DiffContent.class.getDeclaredFields()) {
-                String diffCode = getDiffItem(diffField, codeFinderDiff.getContent());
-                if (diffCode != null) {
-                    TreeMap<Integer, Integer> charToLineMapItem = codeFinderDiff.getCharToLineMap();
-                    try {
-                        findCodeLines(diffCode, charToLineMapItem);
-                    }
-                    catch (IllegalArgumentException e) {
-                        log.warn("Could not retrieve line number from charToLineMap.\nDiff Code = {}", diffCode, e);
-                    }
-                }
-            }
+  public GerritCodeRange findCommentedCode(AIChatReplyItem replyItem, int commentedLine) {
+    this.commentedLine = commentedLine;
+    updateCodePattern(replyItem);
+    currentCodeRange = null;
+    closestCodeRange = null;
+    for (CodeFinderDiff codeFinderDiff : codeFinderDiffs) {
+      for (Field diffField : DiffContent.class.getDeclaredFields()) {
+        String diffCode = getDiffItem(diffField, codeFinderDiff.getContent());
+        if (diffCode != null) {
+          TreeMap<Integer, Integer> charToLineMapItem = codeFinderDiff.getCharToLineMap();
+          try {
+            findCodeLines(diffCode, charToLineMapItem);
+          } catch (IllegalArgumentException e) {
+            log.warn(
+                "Could not retrieve line number from charToLineMap.\nDiff Code = {}", diffCode, e);
+          }
         }
-
-        return closestCodeRange;
+      }
     }
 
-    private void updateCodePattern(AIChatReplyItem replyItem) {
-        String commentedCode = replyItem.getCodeSnippet()
-                .replaceAll(BEGINNING_DIFF_REGEX, "")
-                .replaceAll(ENDING_ELLIPSIS_REGEX, "")
-                .trim();
-        String commentedCodeRegex = Pattern.quote(commentedCode);
-        // Generalize the regex to capture snippets where existing sequences of non-printing chars have been modified
-        // from the original code
-        commentedCodeRegex = commentedCodeRegex.replaceAll("\\s+", NON_PRINTING_REPLACEMENT);
-        // Generalize the regex to capture snippets where non-printing chars have been removed from around the
-        // punctuation marks of the original code
-        commentedCodeRegex = commentedCodeRegex.replaceAll(PUNCTUATION_REGEX, PUNCTUATION_REPLACEMENT);
-        // Remove redundant empty literal escape sequences that could have resulted from previous substitutions
-        commentedCodeRegex = commentedCodeRegex.replaceAll("\\\\Q\\\\E", "");
-        // Obtain a functional regex to match code snippets without relying on non-printing chars
-        commentedCodeRegex = commentedCodeRegex.replaceAll(PLACEHOLDER_REGEX, "\\\\s*");
-        // Remove any detected trailing matching sequence of non-printing chars
-        commentedCodeRegex = commentedCodeRegex.replaceAll("\\\\s\\*$", "");
-        commentedCodePattern = Pattern.compile(commentedCodeRegex);
-    }
+    return closestCodeRange;
+  }
 
-    private double calcCodeDistance(GerritCodeRange range, int fromLine) {
-        return Math.abs((range.endLine - range.startLine) / 2 - fromLine);
-    }
+  private void updateCodePattern(AIChatReplyItem replyItem) {
+    String commentedCode =
+        replyItem
+            .getCodeSnippet()
+            .replaceAll(BEGINNING_DIFF_REGEX, "")
+            .replaceAll(ENDING_ELLIPSIS_REGEX, "")
+            .trim();
+    String commentedCodeRegex = Pattern.quote(commentedCode);
+    // Generalize the regex to capture snippets where existing sequences of non-printing chars have
+    // been modified
+    // from the original code
+    commentedCodeRegex = commentedCodeRegex.replaceAll("\\s+", NON_PRINTING_REPLACEMENT);
+    // Generalize the regex to capture snippets where non-printing chars have been removed from
+    // around the
+    // punctuation marks of the original code
+    commentedCodeRegex = commentedCodeRegex.replaceAll(PUNCTUATION_REGEX, PUNCTUATION_REPLACEMENT);
+    // Remove redundant empty literal escape sequences that could have resulted from previous
+    // substitutions
+    commentedCodeRegex = commentedCodeRegex.replaceAll("\\\\Q\\\\E", "");
+    // Obtain a functional regex to match code snippets without relying on non-printing chars
+    commentedCodeRegex = commentedCodeRegex.replaceAll(PLACEHOLDER_REGEX, "\\\\s*");
+    // Remove any detected trailing matching sequence of non-printing chars
+    commentedCodeRegex = commentedCodeRegex.replaceAll("\\\\s\\*$", "");
+    commentedCodePattern = Pattern.compile(commentedCodeRegex);
+  }
 
-    private String getDiffItem(Field diffField, DiffContent diffItem) {
-        try {
-            return (String) diffField.get(diffItem);
-        }
-        catch (IllegalAccessException e) {
-            log.error("Error while processing file difference (diff type: {})", diffField.getName(), e);
-            return null;
-        }
-    }
+  private double calcCodeDistance(GerritCodeRange range, int fromLine) {
+    return Math.abs((range.endLine - range.startLine) / 2 - fromLine);
+  }
 
-    private int getLineNumber(TreeMap<Integer, Integer> charToLineMapItem, int position) {
-        Integer floorPosition = charToLineMapItem.floorKey(position);
-        if (floorPosition == null) {
-            throw new IllegalArgumentException("Position: " + position);
-        }
-        return charToLineMapItem.get(floorPosition);
+  private String getDiffItem(Field diffField, DiffContent diffItem) {
+    try {
+      return (String) diffField.get(diffItem);
+    } catch (IllegalAccessException e) {
+      log.error("Error while processing file difference (diff type: {})", diffField.getName(), e);
+      return null;
     }
+  }
 
-    private int getLineCharacter(String diffCode, int position) {
-        // Return the offset relative to the nearest preceding newline character if found, `position` otherwise
-        return position - diffCode.substring(0, position).lastIndexOf("\n") -1;
+  private int getLineNumber(TreeMap<Integer, Integer> charToLineMapItem, int position) {
+    Integer floorPosition = charToLineMapItem.floorKey(position);
+    if (floorPosition == null) {
+      throw new IllegalArgumentException("Position: " + position);
     }
+    return charToLineMapItem.get(floorPosition);
+  }
 
-    private void findCodeLines(String diffCode, TreeMap<Integer, Integer> charToLineMapItem)
-            throws IllegalArgumentException {
-        Matcher codeMatcher = commentedCodePattern.matcher(diffCode);
-        while (codeMatcher.find()) {
-            int startPosition = codeMatcher.start();
-            int endPosition = codeMatcher.end();
-            int startLine = getLineNumber(charToLineMapItem, startPosition);
-            int endLine = getLineNumber(charToLineMapItem, endPosition);
-            if (startLine > endLine) {
-                log.info("Code range discarded: start line ({}) greater than end line ({}).\ncodeMatcher: {}.\n" +
-                        "diffCode: {}", startLine, endLine, codeMatcher, diffCode);
-                continue;
-            }
-            int startCharacter = getLineCharacter(diffCode, startPosition);
-            int endCharacter = getLineCharacter(diffCode, endPosition);
-            if (startLine == endLine && startCharacter > endCharacter) {
-                log.info("Code range discarded: start char ({}) greater than end char ({}) for line {}.\ncodeMatcher:" +
-                        " {}.\ndiffCode: {}", startCharacter, endCharacter, startLine, codeMatcher, diffCode);
-                continue;
-            }
-            currentCodeRange = GerritCodeRange.builder()
-                    .startLine(startLine)
-                    .endLine(endLine)
-                    .startCharacter(startCharacter)
-                    .endCharacter(endCharacter)
-                    .build();
-            // If multiple commented code portions are found and currentCommentRange is closer to the line
-            // number suggested by ChatGPT than closestCommentRange, it becomes the new closestCommentRange
-            if (closestCodeRange == null || calcCodeDistance(currentCodeRange, commentedLine) <
-                    calcCodeDistance(closestCodeRange, commentedLine)) {
-                closestCodeRange = currentCodeRange.toBuilder().build();
-            }
-        }
+  private int getLineCharacter(String diffCode, int position) {
+    // Return the offset relative to the nearest preceding newline character if found, `position`
+    // otherwise
+    return position - diffCode.substring(0, position).lastIndexOf("\n") - 1;
+  }
+
+  private void findCodeLines(String diffCode, TreeMap<Integer, Integer> charToLineMapItem)
+      throws IllegalArgumentException {
+    Matcher codeMatcher = commentedCodePattern.matcher(diffCode);
+    while (codeMatcher.find()) {
+      int startPosition = codeMatcher.start();
+      int endPosition = codeMatcher.end();
+      int startLine = getLineNumber(charToLineMapItem, startPosition);
+      int endLine = getLineNumber(charToLineMapItem, endPosition);
+      if (startLine > endLine) {
+        log.info(
+            "Code range discarded: start line ({}) greater than end line ({}).\ncodeMatcher: {}.\n"
+                + "diffCode: {}",
+            startLine,
+            endLine,
+            codeMatcher,
+            diffCode);
+        continue;
+      }
+      int startCharacter = getLineCharacter(diffCode, startPosition);
+      int endCharacter = getLineCharacter(diffCode, endPosition);
+      if (startLine == endLine && startCharacter > endCharacter) {
+        log.info(
+            "Code range discarded: start char ({}) greater than end char ({}) for line {}.\n"
+                + "codeMatcher: {}.\n"
+                + "diffCode: {}",
+            startCharacter,
+            endCharacter,
+            startLine,
+            codeMatcher,
+            diffCode);
+        continue;
+      }
+      currentCodeRange =
+          GerritCodeRange.builder()
+              .startLine(startLine)
+              .endLine(endLine)
+              .startCharacter(startCharacter)
+              .endCharacter(endCharacter)
+              .build();
+      // If multiple commented code portions are found and currentCommentRange is closer to the line
+      // number suggested by ChatGPT than closestCommentRange, it becomes the new
+      // closestCommentRange
+      if (closestCodeRange == null
+          || calcCodeDistance(currentCodeRange, commentedLine)
+              < calcCodeDistance(closestCodeRange, commentedLine)) {
+        closestCodeRange = currentCodeRange.toBuilder().build();
+      }
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/InlineCode.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/InlineCode.java
index 4fa8382..74d948a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/InlineCode.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/code/InlineCode.java
@@ -1,89 +1,100 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.code;
 
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritCodeRange;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
 import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
 
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritCodeRange;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
+
 @Slf4j
 public class InlineCode {
-    private final CodeFinder codeFinder;
-    private final List<String> newContent;
-    private GerritCodeRange range;
+  private final CodeFinder codeFinder;
+  private final List<String> newContent;
+  private GerritCodeRange range;
 
-    public InlineCode(FileDiffProcessed fileDiffProcessed) {
-        codeFinder = new CodeFinder(fileDiffProcessed.getCodeFinderDiffs(), fileDiffProcessed.getRandomPlaceholder());
-        newContent = fileDiffProcessed.getNewContent();
+  public InlineCode(FileDiffProcessed fileDiffProcessed) {
+    codeFinder =
+        new CodeFinder(
+            fileDiffProcessed.getCodeFinderDiffs(), fileDiffProcessed.getRandomPlaceholder());
+    newContent = fileDiffProcessed.getNewContent();
+  }
+
+  public String getInlineCode(GerritComment commentProperty) {
+    if (commentProperty.getRange() != null) {
+      List<String> codeByRange = new ArrayList<>();
+      range = commentProperty.getRange();
+      for (int line_num = range.startLine; line_num <= range.endLine; line_num++) {
+        codeByRange.add(getLineSlice(line_num));
+      }
+      return joinWithNewLine(codeByRange);
+    } else {
+      return getLineFromLineNumber(commentProperty.getLine());
+    }
+  }
+
+  public Optional<GerritCodeRange> findCommentRange(AIChatReplyItem replyItem) {
+    int commentedLine;
+    try {
+      commentedLine = replyItem.getLineNumber();
+    } catch (NumberFormatException ex) {
+      // If the line number is not passed, a line in the middle of the code is used as best guess
+      commentedLine = newContent.size() / 2;
     }
 
-    public String getInlineCode(GerritComment commentProperty) {
-        if (commentProperty.getRange() != null) {
-            List<String> codeByRange = new ArrayList<>();
-            range = commentProperty.getRange();
-            for (int line_num = range.startLine; line_num <= range.endLine; line_num++) {
-                codeByRange.add(getLineSlice(line_num));
-            }
-            return joinWithNewLine(codeByRange);
-        }
-        else {
-            return getLineFromLineNumber(commentProperty.getLine());
-        }
-    }
+    return Optional.ofNullable(codeFinder.findCommentedCode(replyItem, commentedLine));
+  }
 
-    public Optional<GerritCodeRange> findCommentRange(AIChatReplyItem replyItem) {
-        int commentedLine;
-        try {
-            commentedLine = replyItem.getLineNumber();
-        }
-        catch (NumberFormatException ex){
-            // If the line number is not passed, a line in the middle of the code is used as best guess
-            commentedLine = newContent.size() / 2;
-        }
-
-        return Optional.ofNullable(codeFinder.findCommentedCode(replyItem, commentedLine));
+  private String getLineSlice(int line_num) {
+    String line = getLineFromLineNumber(line_num);
+    if (line == null) {
+      throw new RuntimeException("Error retrieving line number from content");
     }
-
-    private String getLineSlice(int line_num) {
-        String line = getLineFromLineNumber(line_num);
-        if (line == null) {
-            throw new RuntimeException("Error retrieving line number from content");
-        }
-        try {
-            if (line_num == range.endLine) {
-                line = line.substring(0, range.endCharacter);
-            }
-            if (line_num == range.startLine) {
-                line = line.substring(range.startCharacter);
-            }
-        }
-        catch (StringIndexOutOfBoundsException e) {
-            log.info("Could not extract a slice from line \"{}\". The whole line is returned", line);
-        }
-        return line;
+    try {
+      if (line_num == range.endLine) {
+        line = line.substring(0, range.endCharacter);
+      }
+      if (line_num == range.startLine) {
+        line = line.substring(range.startCharacter);
+      }
+    } catch (StringIndexOutOfBoundsException e) {
+      log.info("Could not extract a slice from line \"{}\". The whole line is returned", line);
     }
+    return line;
+  }
 
-    private String getLineFromLineNumber(int line_num) {
-        String line = null;
-        try {
-            line = newContent.get(line_num);
-        }
-        catch (IndexOutOfBoundsException e) {
-            // If the line number returned by ChatGPT exceeds the actual number of lines, return the last line
-            int lastLine = newContent.size() - 1;
-            if (line_num > lastLine) {
-                line = newContent.get(lastLine);
-            }
-            else {
-                log.warn("Could not extract line #{} from the code", line_num);
-            }
-        }
-        return line;
+  private String getLineFromLineNumber(int line_num) {
+    String line = null;
+    try {
+      line = newContent.get(line_num);
+    } catch (IndexOutOfBoundsException e) {
+      // If the line number returned by ChatGPT exceeds the actual number of lines, return the last
+      // line
+      int lastLine = newContent.size() - 1;
+      if (line_num > lastLine) {
+        line = newContent.get(lastLine);
+      } else {
+        log.warn("Could not extract line #{} from the code", line_num);
+      }
     }
+    return line;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/comment/GerritCommentRange.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/comment/GerritCommentRange.java
index 01c92c0..837261a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/comment/GerritCommentRange.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/comment/GerritCommentRange.java
@@ -1,44 +1,60 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.comment;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.code.InlineCode;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritCodeRange;
-import lombok.extern.slf4j.Slf4j;
-
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatReplyItem;
 import java.util.HashMap;
 import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class GerritCommentRange {
-    private final HashMap<String, FileDiffProcessed> fileDiffsProcessed;
+  private final HashMap<String, FileDiffProcessed> fileDiffsProcessed;
 
-    public GerritCommentRange(GerritClient gerritClient, GerritChange change) {
-        fileDiffsProcessed = gerritClient.getFileDiffsProcessed(change);
-    }
+  public GerritCommentRange(GerritClient gerritClient, GerritChange change) {
+    fileDiffsProcessed = gerritClient.getFileDiffsProcessed(change);
+  }
 
-    public Optional<GerritCodeRange> getGerritCommentRange(AIChatReplyItem replyItem) {
-        Optional<GerritCodeRange> gerritCommentRange = Optional.empty();
-        String filename = replyItem.getFilename();
-        if (filename == null || filename.equals("/COMMIT_MSG")) {
-            return gerritCommentRange;
-        }
-        if (replyItem.getCodeSnippet() == null) {
-            log.info("CodeSnippet is null in reply '{}'.", replyItem);
-            return gerritCommentRange;
-        }
-        if (!fileDiffsProcessed.containsKey(filename)) {
-            log.info("Filename '{}' not found for reply '{}'.\nFileDiffsProcessed = {}", filename, replyItem,
-                    fileDiffsProcessed);
-            return gerritCommentRange;
-        }
-        InlineCode inlineCode = new InlineCode(fileDiffsProcessed.get(filename));
-        gerritCommentRange = inlineCode.findCommentRange(replyItem);
-        if (gerritCommentRange.isEmpty()) {
-            log.info("Inline code not found for reply {}", replyItem);
-        }
-        return gerritCommentRange;
+  public Optional<GerritCodeRange> getGerritCommentRange(AIChatReplyItem replyItem) {
+    Optional<GerritCodeRange> gerritCommentRange = Optional.empty();
+    String filename = replyItem.getFilename();
+    if (filename == null || filename.equals("/COMMIT_MSG")) {
+      return gerritCommentRange;
     }
+    if (replyItem.getCodeSnippet() == null) {
+      log.info("CodeSnippet is null in reply '{}'.", replyItem);
+      return gerritCommentRange;
+    }
+    if (!fileDiffsProcessed.containsKey(filename)) {
+      log.info(
+          "Filename '{}' not found for reply '{}'.\nFileDiffsProcessed = {}",
+          filename,
+          replyItem,
+          fileDiffsProcessed);
+      return gerritCommentRange;
+    }
+    InlineCode inlineCode = new InlineCode(fileDiffsProcessed.get(filename));
+    gerritCommentRange = inlineCode.findCommentRange(replyItem);
+    if (gerritCommentRange.isEmpty()) {
+      log.info("Inline code not found for reply {}", replyItem);
+    }
+    return gerritCommentRange;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/diff/FileDiffProcessed.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/diff/FileDiffProcessed.java
index 8fa74bc..b999b02 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/diff/FileDiffProcessed.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/patch/diff/FileDiffProcessed.java
@@ -1,133 +1,155 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritPatchSetFileDiff;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.code.CodeFinderDiff;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.diff.DiffContent;
 import com.googlesource.gerrit.plugins.aicodereview.settings.Settings;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.RandomStringUtils;
-
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.TreeMap;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomStringUtils;
 
 @Slf4j
 public class FileDiffProcessed {
-    private static final int MIN_RANDOM_PLACEHOLDER_VARIABLE_LENGTH = 1;
+  private static final int MIN_RANDOM_PLACEHOLDER_VARIABLE_LENGTH = 1;
 
-    private final Configuration config;
-    private final boolean isCommitMessage;
-    @Getter
-    private List<CodeFinderDiff> codeFinderDiffs;
-    @Getter
-    private List<String> newContent;
-    @Getter
-    private List<DiffContent> reviewDiffContent;
-    @Getter
-    private String randomPlaceholder;
-    private int lineNum;
-    private DiffContent diffContentItem;
-    private DiffContent reviewDiffContentItem;
-    private TreeMap<Integer, Integer> charToLineMapItem;
+  private final Configuration config;
+  private final boolean isCommitMessage;
+  @Getter private List<CodeFinderDiff> codeFinderDiffs;
+  @Getter private List<String> newContent;
+  @Getter private List<DiffContent> reviewDiffContent;
+  @Getter private String randomPlaceholder;
+  private int lineNum;
+  private DiffContent diffContentItem;
+  private DiffContent reviewDiffContentItem;
+  private TreeMap<Integer, Integer> charToLineMapItem;
 
-    public FileDiffProcessed(Configuration config, boolean isCommitMessage,
-                             GerritPatchSetFileDiff gerritPatchSetFileDiff) {
-        this.config = config;
-        this.isCommitMessage = isCommitMessage;
+  public FileDiffProcessed(
+      Configuration config,
+      boolean isCommitMessage,
+      GerritPatchSetFileDiff gerritPatchSetFileDiff) {
+    this.config = config;
+    this.isCommitMessage = isCommitMessage;
 
-        updateContent(gerritPatchSetFileDiff);
-        updateRandomPlaceholder(gerritPatchSetFileDiff);
-    }
+    updateContent(gerritPatchSetFileDiff);
+    updateRandomPlaceholder(gerritPatchSetFileDiff);
+  }
 
-    private void updateContent(GerritPatchSetFileDiff gerritPatchSetFileDiff) {
-        newContent = new ArrayList<>() {{
+  private void updateContent(GerritPatchSetFileDiff gerritPatchSetFileDiff) {
+    newContent =
+        new ArrayList<>() {
+          {
             add("DUMMY LINE #0");
-        }};
-        lineNum = 1;
-        reviewDiffContent = new ArrayList<>();
-        codeFinderDiffs = new ArrayList<>();
-        List<GerritPatchSetFileDiff.Content> patchSetDiffContent = gerritPatchSetFileDiff.getContent();
-        // Iterate over the items of the diff content
-        for (GerritPatchSetFileDiff.Content patchSetContentItem : patchSetDiffContent) {
-            diffContentItem = new DiffContent();
-            reviewDiffContentItem = new DiffContent();
-            charToLineMapItem = new TreeMap<>();
-            // Iterate over the fields `a`, `b` and `ab` of each diff content
-            for (Field patchSetDiffField : GerritPatchSetFileDiff.Content.class.getDeclaredFields()) {
-                processFileDiffItem(patchSetDiffField, patchSetContentItem);
-            }
-            reviewDiffContent.add(reviewDiffContentItem);
-            codeFinderDiffs.add(new CodeFinderDiff(diffContentItem, charToLineMapItem));
-        }
+          }
+        };
+    lineNum = 1;
+    reviewDiffContent = new ArrayList<>();
+    codeFinderDiffs = new ArrayList<>();
+    List<GerritPatchSetFileDiff.Content> patchSetDiffContent = gerritPatchSetFileDiff.getContent();
+    // Iterate over the items of the diff content
+    for (GerritPatchSetFileDiff.Content patchSetContentItem : patchSetDiffContent) {
+      diffContentItem = new DiffContent();
+      reviewDiffContentItem = new DiffContent();
+      charToLineMapItem = new TreeMap<>();
+      // Iterate over the fields `a`, `b` and `ab` of each diff content
+      for (Field patchSetDiffField : GerritPatchSetFileDiff.Content.class.getDeclaredFields()) {
+        processFileDiffItem(patchSetDiffField, patchSetContentItem);
+      }
+      reviewDiffContent.add(reviewDiffContentItem);
+      codeFinderDiffs.add(new CodeFinderDiff(diffContentItem, charToLineMapItem));
+    }
+  }
+
+  private void updateRandomPlaceholder(GerritPatchSetFileDiff gerritPatchSetFileDiff) {
+    int placeholderVariableLength = MIN_RANDOM_PLACEHOLDER_VARIABLE_LENGTH;
+    do {
+      randomPlaceholder = '#' + RandomStringUtils.random(placeholderVariableLength, true, false);
+      placeholderVariableLength++;
+    } while (gerritPatchSetFileDiff.toString().contains(randomPlaceholder));
+  }
+
+  private void filterCommitMessageContent(List<String> fieldValue) {
+    fieldValue.removeIf(
+        s ->
+            s.isEmpty()
+                || Settings.COMMIT_MESSAGE_FILTER_OUT_PREFIXES.values().stream()
+                    .anyMatch(s::startsWith));
+  }
+
+  private void updateCodeEntities(Field diffField, List<String> diffLines)
+      throws IllegalAccessException {
+    String diffType = diffField.getName();
+    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
+    if (diffType.contains("b")) {
+      int diffCharPointer = -1;
+      for (String diffLine : diffLines) {
+        // Increase of 1 to take into account of the newline character
+        diffCharPointer++;
+        charToLineMapItem.put(diffCharPointer, lineNum);
+        diffCharPointer += diffLine.length();
+        lineNum++;
+      }
+      // Add the last line to charToLineMapItem
+      charToLineMapItem.put(diffCharPointer + 1, lineNum);
+      newContent.addAll(diffLines);
+    }
+    // If the lines modified in the PatchSet are deleted, they are mapped in charToLineMapItem to
+    // current lineNum
+    else {
+      int startingPosition = charToLineMapItem.isEmpty() ? 0 : content.length();
+      charToLineMapItem.put(startingPosition, lineNum);
     }
 
-    private void updateRandomPlaceholder(GerritPatchSetFileDiff gerritPatchSetFileDiff) {
-        int placeholderVariableLength = MIN_RANDOM_PLACEHOLDER_VARIABLE_LENGTH;
-        do {
-            randomPlaceholder = '#' + RandomStringUtils.random(placeholderVariableLength, true, false);
-            placeholderVariableLength++;
-        }
-        while (gerritPatchSetFileDiff.toString().contains(randomPlaceholder));
+    if (config.getGptFullFileReview() || !diffType.equals("ab")) {
+      // Store the new field's value in the diff content for the Patch Set review
+      // `reviewDiffContentItem`
+      diffField.set(reviewDiffContentItem, content);
     }
+  }
 
-    private void filterCommitMessageContent(List<String> fieldValue) {
-        fieldValue.removeIf(s ->
-                s.isEmpty() || Settings.COMMIT_MESSAGE_FILTER_OUT_PREFIXES.values().stream().anyMatch(s::startsWith));
+  private void processFileDiffItem(
+      Field patchSetDiffField, GerritPatchSetFileDiff.Content contentItem) {
+    try {
+      // Get the `a`, `b` or `ab` field's value from the Patch Set diff content
+      @SuppressWarnings("unchecked")
+      List<String> diffLines = (List<String>) patchSetDiffField.get(contentItem);
+      if (diffLines == null) {
+        return;
+      }
+      if (isCommitMessage) {
+        filterCommitMessageContent(diffLines);
+      }
+      // Get the corresponding `a`, `b` or `ab` field from the DiffContent class
+      Field diffField = DiffContent.class.getDeclaredField(patchSetDiffField.getName());
+      updateCodeEntities(diffField, diffLines);
+
+    } catch (IllegalAccessException | NoSuchFieldException e) {
+      log.error(
+          "Error while processing file difference (diff type: {})", patchSetDiffField.getName(), e);
     }
-
-    private void updateCodeEntities(Field diffField, List<String> diffLines) throws IllegalAccessException {
-        String diffType = diffField.getName();
-        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
-        if (diffType.contains("b")) {
-            int diffCharPointer = -1;
-            for (String diffLine : diffLines) {
-                // Increase of 1 to take into account of the newline character
-                diffCharPointer++;
-                charToLineMapItem.put(diffCharPointer, lineNum);
-                diffCharPointer += diffLine.length();
-                lineNum++;
-            }
-            // Add the last line to charToLineMapItem
-            charToLineMapItem.put(diffCharPointer +1, lineNum);
-            newContent.addAll(diffLines);
-        }
-        // If the lines modified in the PatchSet are deleted, they are mapped in charToLineMapItem to current lineNum
-        else {
-            int startingPosition = charToLineMapItem.isEmpty() ? 0 : content.length();
-            charToLineMapItem.put(startingPosition, lineNum);
-        }
-
-        if (config.getGptFullFileReview() || !diffType.equals("ab")) {
-            // Store the new field's value in the diff content for the Patch Set review `reviewDiffContentItem`
-            diffField.set(reviewDiffContentItem, content);
-        }
-    }
-
-    private void processFileDiffItem(Field patchSetDiffField, GerritPatchSetFileDiff.Content contentItem) {
-        try {
-            // Get the `a`, `b` or `ab` field's value from the Patch Set diff content
-            @SuppressWarnings("unchecked")
-            List<String> diffLines = (List<String>) patchSetDiffField.get(contentItem);
-            if (diffLines == null) {
-                return;
-            }
-            if (isCommitMessage) {
-                filterCommitMessageContent(diffLines);
-            }
-            // Get the corresponding `a`, `b` or `ab` field from the DiffContent class
-            Field diffField = DiffContent.class.getDeclaredField(patchSetDiffField.getName());
-            updateCodeEntities(diffField, diffLines);
-
-        } catch (IllegalAccessException | NoSuchFieldException e) {
-            log.error("Error while processing file difference (diff type: {})", patchSetDiffField.getName(), e);
-        }
-    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatComment.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatComment.java
index be5ef0b..ccf9747 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatComment.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -10,29 +24,29 @@
 
 @Slf4j
 public class AIChatComment extends ClientBase {
-    protected ClientMessage commentMessage;
+  protected ClientMessage commentMessage;
 
-    private final ChangeSetData changeSetData;
-    private final Localizer localizer;
+  private final ChangeSetData changeSetData;
+  private final Localizer localizer;
 
-    public AIChatComment(Configuration config, ChangeSetData changeSetData, Localizer localizer) {
-        super(config);
-        this.changeSetData = changeSetData;
-        this.localizer = localizer;
+  public AIChatComment(Configuration config, ChangeSetData changeSetData, Localizer localizer) {
+    super(config);
+    this.changeSetData = changeSetData;
+    this.localizer = localizer;
+  }
+
+  protected String getCleanedMessage(GerritComment commentProperty) {
+    commentMessage =
+        new ClientMessage(config, changeSetData, commentProperty.getMessage(), localizer);
+    if (isFromAssistant(commentProperty)) {
+      commentMessage.removeDebugCodeBlocksReview().removeDebugCodeBlocksDynamicSettings();
+    } else {
+      commentMessage.removeMentions().parseRemoveCommands();
     }
+    return commentMessage.removeHeadings().getMessage();
+  }
 
-    protected String getCleanedMessage(GerritComment commentProperty) {
-        commentMessage = new ClientMessage(config, changeSetData, commentProperty.getMessage(), localizer);
-        if (isFromAssistant(commentProperty)) {
-            commentMessage.removeDebugCodeBlocksReview().removeDebugCodeBlocksDynamicSettings();
-        }
-        else {
-            commentMessage.removeMentions().parseRemoveCommands();
-        }
-        return commentMessage.removeHeadings().getMessage();
-    }
-
-    protected boolean isFromAssistant(GerritComment commentProperty) {
-        return commentProperty.getAuthor().getAccountId() == changeSetData.getGptAccountId();
-    }
+  protected boolean isFromAssistant(GerritComment commentProperty) {
+    return commentProperty.getAuthor().getAccountId() == changeSetData.getGptAccountId();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPrompt.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPrompt.java
index 928e385..f1b2624 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPrompt.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPrompt.java
@@ -1,5 +1,21 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.prompt.ChatAIDataPrompt;
 import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
@@ -7,37 +23,29 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatMessageItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class AIChatDataPrompt {
-    private final ChatAIDataPrompt aiChatDataPromptHandler;
+  private final ChatAIDataPrompt aiChatDataPromptHandler;
 
-    public AIChatDataPrompt(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        aiChatDataPromptHandler = AIChatPromptFactory.getChatGptDataPrompt(
-                config,
-                changeSetData,
-                change,
-                gerritClientData,
-                localizer
-        );
-    }
+  public AIChatDataPrompt(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    aiChatDataPromptHandler =
+        AIChatPromptFactory.getChatGptDataPrompt(
+            config, changeSetData, change, gerritClientData, localizer);
+  }
 
-    public String buildPrompt() {
-        for (int i = 0; i < aiChatDataPromptHandler.getCommentProperties().size(); i++) {
-            aiChatDataPromptHandler.addMessageItem(i);
-        }
-        List<AIChatMessageItem> messageItems = aiChatDataPromptHandler.getMessageItems();
-        return messageItems.isEmpty() ? "" : getGson().toJson(messageItems);
+  public String buildPrompt() {
+    for (int i = 0; i < aiChatDataPromptHandler.getCommentProperties().size(); i++) {
+      aiChatDataPromptHandler.addMessageItem(i);
     }
+    List<AIChatMessageItem> messageItems = aiChatDataPromptHandler.getMessageItems();
+    return messageItems.isEmpty() ? "" : getGson().toJson(messageItems);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptBase.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptBase.java
index 6ef3bf2..2473bb5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptBase.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -5,67 +19,64 @@
 import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.code.InlineCode;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatMessageItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.CommentData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public abstract class AIChatDataPromptBase implements ChatAIDataPrompt {
-    protected final GerritClientData gerritClientData;
-    protected final HashMap<String, FileDiffProcessed> fileDiffsProcessed;
-    protected final CommentData commentData;
-    @Getter
-    protected final List<AIChatMessageItem> messageItems;
+  protected final GerritClientData gerritClientData;
+  protected final HashMap<String, FileDiffProcessed> fileDiffsProcessed;
+  protected final CommentData commentData;
+  @Getter protected final List<AIChatMessageItem> messageItems;
 
-    protected AIChatHistory aiChatHistory;
-    @Getter
-    protected List<GerritComment> commentProperties;
+  protected AIChatHistory aiChatHistory;
+  @Getter protected List<GerritComment> commentProperties;
 
-    public AIChatDataPromptBase(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        this.gerritClientData = gerritClientData;
-        fileDiffsProcessed = gerritClientData.getFileDiffsProcessed();
-        commentData = gerritClientData.getCommentData();
-        aiChatHistory = new AIChatHistory(config, changeSetData, gerritClientData, localizer);
-        messageItems = new ArrayList<>();
-    }
+  public AIChatDataPromptBase(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    this.gerritClientData = gerritClientData;
+    fileDiffsProcessed = gerritClientData.getFileDiffsProcessed();
+    commentData = gerritClientData.getCommentData();
+    aiChatHistory = new AIChatHistory(config, changeSetData, gerritClientData, localizer);
+    messageItems = new ArrayList<>();
+  }
 
-    public abstract void addMessageItem(int i);
+  public abstract void addMessageItem(int i);
 
-    protected AIChatMessageItem getMessageItem(int i) {
-        AIChatMessageItem messageItem = new AIChatMessageItem();
-        GerritComment commentProperty = commentProperties.get(i);
-        if (commentProperty.getLine() != null || commentProperty.getRange() != null) {
-            String filename = commentProperty.getFilename();
-            FileDiffProcessed fileDiffProcessed = fileDiffsProcessed.get(filename);
-            if (fileDiffProcessed == null) {
-                return messageItem;
-            }
-            InlineCode inlineCode = new InlineCode(fileDiffProcessed);
-            messageItem.setFilename(filename);
-            messageItem.setLineNumber(commentProperty.getLine());
-            messageItem.setCodeSnippet(inlineCode.getInlineCode(commentProperty));
-        }
-
+  protected AIChatMessageItem getMessageItem(int i) {
+    AIChatMessageItem messageItem = new AIChatMessageItem();
+    GerritComment commentProperty = commentProperties.get(i);
+    if (commentProperty.getLine() != null || commentProperty.getRange() != null) {
+      String filename = commentProperty.getFilename();
+      FileDiffProcessed fileDiffProcessed = fileDiffsProcessed.get(filename);
+      if (fileDiffProcessed == null) {
         return messageItem;
+      }
+      InlineCode inlineCode = new InlineCode(fileDiffProcessed);
+      messageItem.setFilename(filename);
+      messageItem.setLineNumber(commentProperty.getLine());
+      messageItem.setCodeSnippet(inlineCode.getInlineCode(commentProperty));
     }
 
-    protected void setHistory(AIChatMessageItem messageItem, List<AIChatRequestMessage> messageHistory) {
-        if (!messageHistory.isEmpty()) {
-            messageItem.setHistory(messageHistory);
-        }
+    return messageItem;
+  }
+
+  protected void setHistory(
+      AIChatMessageItem messageItem, List<AIChatRequestMessage> messageHistory) {
+    if (!messageHistory.isEmpty()) {
+      messageItem.setHistory(messageHistory);
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptRequests.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptRequests.java
index 708aa3a..1bf8b8f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptRequests.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptRequests.java
@@ -1,54 +1,67 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.OPEN_AI_CHAT_ROLE_USER;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatMessageItem;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.OPEN_AI_CHAT_ROLE_USER;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class AIChatDataPromptRequests extends AIChatDataPromptBase {
-    protected AIChatMessageItem messageItem;
-    protected List<AIChatRequestMessage> messageHistory;
+  protected AIChatMessageItem messageItem;
+  protected List<AIChatRequestMessage> messageHistory;
 
-    public AIChatDataPromptRequests(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        super(config, changeSetData, gerritClientData, localizer);
-        commentProperties = commentData.getCommentProperties();
+  public AIChatDataPromptRequests(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    super(config, changeSetData, gerritClientData, localizer);
+    commentProperties = commentData.getCommentProperties();
+  }
+
+  public void addMessageItem(int i) {
+    AIChatMessageItem messageItem = getMessageItem(i);
+    messageItem.setId(i);
+    messageItems.add(messageItem);
+  }
+
+  protected AIChatMessageItem getMessageItem(int i) {
+    messageItem = super.getMessageItem(i);
+    messageHistory = aiChatHistory.retrieveHistory(commentProperties.get(i));
+    AIChatRequestMessage request = extractLastUserMessageFromHistory();
+    messageItem.setRequest(request.getContent());
+
+    return messageItem;
+  }
+
+  private AIChatRequestMessage extractLastUserMessageFromHistory() {
+    for (int i = messageHistory.size() - 1; i >= 0; i--) {
+      if (OPEN_AI_CHAT_ROLE_USER.equals(messageHistory.get(i).getRole())) {
+        return messageHistory.remove(i);
+      }
     }
-
-    public void addMessageItem(int i) {
-        AIChatMessageItem messageItem = getMessageItem(i);
-        messageItem.setId(i);
-        messageItems.add(messageItem);
-    }
-
-    protected AIChatMessageItem getMessageItem(int i) {
-        messageItem = super.getMessageItem(i);
-        messageHistory = aiChatHistory.retrieveHistory(commentProperties.get(i));
-        AIChatRequestMessage request = extractLastUserMessageFromHistory();
-        messageItem.setRequest(request.getContent());
-
-        return messageItem;
-    }
-
-    private AIChatRequestMessage extractLastUserMessageFromHistory() {
-        for (int i = messageHistory.size() - 1; i >= 0; i--) {
-            if (OPEN_AI_CHAT_ROLE_USER.equals(messageHistory.get(i).getRole())) {
-                return messageHistory.remove(i);
-            }
-        }
-        throw new RuntimeException("Error extracting request from message history: no user message found in " +
-                messageHistory);
-    }
+    throw new RuntimeException(
+        "Error extracting request from message history: no user message found in "
+            + messageHistory);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptReview.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptReview.java
index f49663f..acaa7e5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatDataPromptReview.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -7,36 +21,34 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ArrayList;
 import java.util.List;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class AIChatDataPromptReview extends AIChatDataPromptBase implements ChatAIDataPrompt {
-    public AIChatDataPromptReview(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        super(config, changeSetData, gerritClientData, localizer);
-        commentProperties = new ArrayList<>(commentData.getCommentMap().values());
-    }
+  public AIChatDataPromptReview(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    super(config, changeSetData, gerritClientData, localizer);
+    commentProperties = new ArrayList<>(commentData.getCommentMap().values());
+  }
 
-    public void addMessageItem(int i) {
-        AIChatMessageItem messageItem = getMessageItem(i);
-        if (messageItem.getHistory() != null) {
-            messageItems.add(messageItem);
-        }
+  public void addMessageItem(int i) {
+    AIChatMessageItem messageItem = getMessageItem(i);
+    if (messageItem.getHistory() != null) {
+      messageItems.add(messageItem);
     }
+  }
 
-    protected AIChatMessageItem getMessageItem(int i) {
-        AIChatMessageItem messageItem = super.getMessageItem(i);
-        List<AIChatRequestMessage> messageHistory = aiChatHistory.retrieveHistory(commentProperties.get(i),
-                true);
-        setHistory(messageItem, messageHistory);
+  protected AIChatMessageItem getMessageItem(int i) {
+    AIChatMessageItem messageItem = super.getMessageItem(i);
+    List<AIChatRequestMessage> messageHistory =
+        aiChatHistory.retrieveHistory(commentProperties.get(i), true);
+    setHistory(messageItem, messageHistory);
 
-        return messageItem;
-    }
+    return messageItem;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatGptPrompt.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatGptPrompt.java
index d3d2a51..b0f83ab 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatGptPrompt.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatGptPrompt.java
@@ -1,151 +1,184 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.INLINE_CODE_DELIMITER;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.SPACE;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithComma;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithSemicolon;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithSpace;
+
 import com.google.gson.reflect.TypeToken;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.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.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class AIChatGptPrompt {
-    // Reply attributes
-    public static final String ATTRIBUTE_ID = "id";
-    public static final String ATTRIBUTE_REPLY = "reply";
-    public static final String ATTRIBUTE_SCORE = "score";
-    public static final String ATTRIBUTE_REPEATED = "repeated";
-    public static final String ATTRIBUTE_CONFLICTING = "conflicting";
-    public static final String ATTRIBUTE_RELEVANCE = "relevance";
-    public static final String ATTRIBUTE_CHANGE_ID = "changeId";
-    public static final List<String> PATCH_SET_REVIEW_REPLY_ATTRIBUTES = new ArrayList<>(Arrays.asList(
-            ATTRIBUTE_REPLY, ATTRIBUTE_SCORE, ATTRIBUTE_REPEATED, ATTRIBUTE_CONFLICTING, ATTRIBUTE_RELEVANCE
-    ));
-    public static final List<String> REQUEST_REPLY_ATTRIBUTES = new ArrayList<>(Arrays.asList(
-            ATTRIBUTE_REPLY, ATTRIBUTE_ID, ATTRIBUTE_CHANGE_ID
-    ));
+  // Reply attributes
+  public static final String ATTRIBUTE_ID = "id";
+  public static final String ATTRIBUTE_REPLY = "reply";
+  public static final String ATTRIBUTE_SCORE = "score";
+  public static final String ATTRIBUTE_REPEATED = "repeated";
+  public static final String ATTRIBUTE_CONFLICTING = "conflicting";
+  public static final String ATTRIBUTE_RELEVANCE = "relevance";
+  public static final String ATTRIBUTE_CHANGE_ID = "changeId";
+  public static final List<String> PATCH_SET_REVIEW_REPLY_ATTRIBUTES =
+      new ArrayList<>(
+          Arrays.asList(
+              ATTRIBUTE_REPLY,
+              ATTRIBUTE_SCORE,
+              ATTRIBUTE_REPEATED,
+              ATTRIBUTE_CONFLICTING,
+              ATTRIBUTE_RELEVANCE));
+  public static final List<String> REQUEST_REPLY_ATTRIBUTES =
+      new ArrayList<>(Arrays.asList(ATTRIBUTE_REPLY, ATTRIBUTE_ID, ATTRIBUTE_CHANGE_ID));
 
-    // Prompt constants loaded from JSON file
-    public static String DEFAULT_AI_CHAT_SYSTEM_PROMPT;
-    public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_DIRECTIVES;
-    public static String DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT;
-    public static String DEFAULT_AI_CHAT_REPLIES_PROMPT_SPECS;
-    public static String DEFAULT_AI_CHAT_REPLIES_PROMPT_INLINE;
-    public static String DEFAULT_AI_CHAT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK;
-    public static String DEFAULT_AI_CHAT_REQUEST_PROMPT_DIFF;
-    public static String DEFAULT_AI_CHAT_REQUEST_PROMPT_REQUESTS;
-    public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_COMMIT_MESSAGES;
-    public static String DEFAULT_AI_CHAT_RELEVANCE_RULES;
-    public static String DEFAULT_AI_CHAT_HOW_TO_FIND_COMMIT_MESSAGE;
-    public static Map<String, String> DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES;
+  // Prompt constants loaded from JSON file
+  public static String DEFAULT_AI_CHAT_SYSTEM_PROMPT;
+  public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_DIRECTIVES;
+  public static String DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT;
+  public static String DEFAULT_AI_CHAT_REPLIES_PROMPT_SPECS;
+  public static String DEFAULT_AI_CHAT_REPLIES_PROMPT_INLINE;
+  public static String DEFAULT_AI_CHAT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK;
+  public static String DEFAULT_AI_CHAT_REQUEST_PROMPT_DIFF;
+  public static String DEFAULT_AI_CHAT_REQUEST_PROMPT_REQUESTS;
+  public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_COMMIT_MESSAGES;
+  public static String DEFAULT_AI_CHAT_RELEVANCE_RULES;
+  public static String DEFAULT_AI_CHAT_HOW_TO_FIND_COMMIT_MESSAGE;
+  public static Map<String, String> DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES;
 
-    protected final Configuration config;
+  protected final Configuration config;
 
-    @Setter
-    protected boolean isCommentEvent;
+  @Setter protected boolean isCommentEvent;
 
-    public AIChatGptPrompt(Configuration config) {
-        this(config, false);
+  public AIChatGptPrompt(Configuration config) {
+    this(config, false);
+  }
+
+  public AIChatGptPrompt(Configuration config, boolean isCommentEvent) {
+    this.config = config;
+    this.isCommentEvent = isCommentEvent;
+    // Avoid repeated loading of prompt constants
+    if (DEFAULT_AI_CHAT_SYSTEM_PROMPT == null) {
+      loadDefaultPrompts("prompts");
     }
+  }
 
-    public AIChatGptPrompt(Configuration config, boolean isCommentEvent) {
-        this.config = config;
-        this.isCommentEvent = isCommentEvent;
-        // Avoid repeated loading of prompt constants
-        if (DEFAULT_AI_CHAT_SYSTEM_PROMPT == null) {
-            loadDefaultPrompts("prompts");
-        }
-    }
-
-    public static String getCommentRequestPrompt(int commentPropertiesSize) {
-        return joinWithSpace(new ArrayList<>(List.of(
+  public static String getCommentRequestPrompt(int commentPropertiesSize) {
+    return joinWithSpace(
+        new ArrayList<>(
+            List.of(
                 DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT,
                 buildFieldSpecifications(REQUEST_REPLY_ATTRIBUTES),
                 DEFAULT_AI_CHAT_REPLIES_PROMPT_INLINE,
-                String.format(DEFAULT_AI_CHAT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK, commentPropertiesSize)
-        )));
-    }
+                String.format(
+                    DEFAULT_AI_CHAT_REPLIES_PROMPT_ENFORCE_RESPONSE_CHECK,
+                    commentPropertiesSize))));
+  }
 
-    public static String getReviewPromptCommitMessages() {
-        return String.format(DEFAULT_AI_CHAT_REVIEW_PROMPT_COMMIT_MESSAGES, DEFAULT_AI_CHAT_HOW_TO_FIND_COMMIT_MESSAGE);
-    }
+  public static String getReviewPromptCommitMessages() {
+    return String.format(
+        DEFAULT_AI_CHAT_REVIEW_PROMPT_COMMIT_MESSAGES, DEFAULT_AI_CHAT_HOW_TO_FIND_COMMIT_MESSAGE);
+  }
 
-    protected void loadDefaultPrompts(String promptFilename) {
-        String promptFile = String.format("config/%s.json", promptFilename);
-        Class<? extends AIChatGptPrompt> me = this.getClass();
-        try (InputStreamReader reader = FileUtils.getInputStreamReader(promptFile)) {
-            Map<String, Object> values = getGson().fromJson(reader, new TypeToken<Map<String, Object>>(){}.getType());
-            for (Map.Entry<String, Object> entry : values.entrySet()) {
-                try {
-                    Field field = me.getField(entry.getKey());
-                    field.setAccessible(true);
-                    field.set(null, entry.getValue());
-                } catch (NoSuchFieldException | IllegalAccessException e) {
-                    log.error("Error setting prompt '{}'", entry.getKey(), e);
-                    throw new IOException();
-                }
-            }
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to load prompts", e);
+  protected void loadDefaultPrompts(String promptFilename) {
+    String promptFile = String.format("config/%s.json", promptFilename);
+    Class<? extends AIChatGptPrompt> me = this.getClass();
+    try (InputStreamReader reader = FileUtils.getInputStreamReader(promptFile)) {
+      Map<String, Object> values =
+          getGson().fromJson(reader, new TypeToken<Map<String, Object>>() {}.getType());
+      for (Map.Entry<String, Object> entry : values.entrySet()) {
+        try {
+          Field field = me.getField(entry.getKey());
+          field.setAccessible(true);
+          field.set(null, entry.getValue());
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+          log.error("Error setting prompt '{}'", entry.getKey(), e);
+          throw new IOException();
         }
-        // Keep the given order of attributes
-        DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES = new LinkedHashMap<>(DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES);
+      }
+    } catch (IOException e) {
+      throw new RuntimeException("Failed to load prompts", e);
     }
+    // Keep the given order of attributes
+    DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES = new LinkedHashMap<>(DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES);
+  }
 
-    protected static String buildFieldSpecifications(List<String> filterFields) {
-        Set<String> orderedFilterFields = new LinkedHashSet<>(filterFields);
-        Map<String, String> attributes = DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.entrySet().stream()
-                .filter(entry -> orderedFilterFields.contains(entry.getKey()))
-                .collect(Collectors.toMap(
-                        entry -> INLINE_CODE_DELIMITER + entry.getKey() + INLINE_CODE_DELIMITER,
-                        Map.Entry::getValue,
-                        (oldValue, newValue) -> oldValue,
-                        LinkedHashMap::new
-                ));
-        List<String> fieldDescription = attributes.entrySet().stream()
-                .map(entry -> entry.getKey() + SPACE + entry.getValue())
-                .collect(Collectors.toList());
+  protected static String buildFieldSpecifications(List<String> filterFields) {
+    Set<String> orderedFilterFields = new LinkedHashSet<>(filterFields);
+    Map<String, String> attributes =
+        DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.entrySet().stream()
+            .filter(entry -> orderedFilterFields.contains(entry.getKey()))
+            .collect(
+                Collectors.toMap(
+                    entry -> INLINE_CODE_DELIMITER + entry.getKey() + INLINE_CODE_DELIMITER,
+                    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_AI_CHAT_REPLIES_PROMPT_SPECS,
-                joinWithComma(attributes.keySet()),
-                joinWithSemicolon(fieldDescription)
-        );
+    return String.format(
+        DEFAULT_AI_CHAT_REPLIES_PROMPT_SPECS,
+        joinWithComma(attributes.keySet()),
+        joinWithSemicolon(fieldDescription));
+  }
+
+  public String getPatchSetReviewPrompt() {
+    List<String> attributes = new ArrayList<>(PATCH_SET_REVIEW_REPLY_ATTRIBUTES);
+    if (config.isVotingEnabled() || config.getFilterNegativeComments()) {
+      updateScoreDescription();
+    } else {
+      attributes.remove(ATTRIBUTE_SCORE);
     }
+    updateRelevanceDescription();
+    return buildFieldSpecifications(attributes) + SPACE + DEFAULT_AI_CHAT_REPLIES_PROMPT_INLINE;
+  }
 
-    public String getPatchSetReviewPrompt() {
-        List<String> attributes = new ArrayList<>(PATCH_SET_REVIEW_REPLY_ATTRIBUTES);
-        if (config.isVotingEnabled() || config.getFilterNegativeComments()) {
-            updateScoreDescription();
-        }
-        else {
-            attributes.remove(ATTRIBUTE_SCORE);
-        }
-        updateRelevanceDescription();
-        return buildFieldSpecifications(attributes) + SPACE + DEFAULT_AI_CHAT_REPLIES_PROMPT_INLINE;
+  private void updateScoreDescription() {
+    String scoreDescription = DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.get(ATTRIBUTE_SCORE);
+    if (scoreDescription.contains("%d")) {
+      scoreDescription =
+          String.format(scoreDescription, config.getVotingMinScore(), config.getVotingMaxScore());
+      DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.put(ATTRIBUTE_SCORE, scoreDescription);
     }
+  }
 
-    private void updateScoreDescription() {
-        String scoreDescription = DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.get(ATTRIBUTE_SCORE);
-        if (scoreDescription.contains("%d")) {
-            scoreDescription = String.format(scoreDescription, config.getVotingMinScore(), config.getVotingMaxScore());
-            DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.put(ATTRIBUTE_SCORE, scoreDescription);
-        }
+  private void updateRelevanceDescription() {
+    String relevanceDescription = DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.get(ATTRIBUTE_RELEVANCE);
+    if (relevanceDescription.contains("%s")) {
+      String defaultAIRelevanceRules =
+          config.getString(Configuration.KEY_AI_RELEVANCE_RULES, DEFAULT_AI_CHAT_RELEVANCE_RULES);
+      relevanceDescription = String.format(relevanceDescription, defaultAIRelevanceRules);
+      DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.put(ATTRIBUTE_RELEVANCE, relevanceDescription);
     }
-
-    private void updateRelevanceDescription() {
-        String relevanceDescription = DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.get(ATTRIBUTE_RELEVANCE);
-        if (relevanceDescription.contains("%s")) {
-            String defaultAIRelevanceRules = config.getString(Configuration.KEY_AI_RELEVANCE_RULES,
-                    DEFAULT_AI_CHAT_RELEVANCE_RULES);
-            relevanceDescription = String.format(relevanceDescription, defaultAIRelevanceRules);
-            DEFAULT_AI_CHAT_REPLIES_ATTRIBUTES.put(ATTRIBUTE_RELEVANCE, relevanceDescription);
-        }
-    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatHistory.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatHistory.java
index f48462c..47e0ab6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatHistory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatHistory.java
@@ -1,148 +1,174 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.CommentData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
 import com.googlesource.gerrit.plugins.aicodereview.settings.Settings;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class AIChatHistory extends AIChatComment {
-    private final Set<String> messagesExcludedFromHistory;
-    private final HashMap<String, GerritComment> commentMap;
-    private final HashMap<String, GerritComment> patchSetCommentMap;
-    private final Set<String> patchSetCommentAdded;
-    private final List<GerritComment> patchSetComments;
-    private final int revisionBase;
+  private final Set<String> messagesExcludedFromHistory;
+  private final HashMap<String, GerritComment> commentMap;
+  private final HashMap<String, GerritComment> patchSetCommentMap;
+  private final Set<String> patchSetCommentAdded;
+  private final List<GerritComment> patchSetComments;
+  private final int revisionBase;
 
-    private boolean filterActive;
+  private boolean filterActive;
 
-    public AIChatHistory(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        super(config, changeSetData, localizer);
-        CommentData commentData = gerritClientData.getCommentData();
-        messagesExcludedFromHistory = Set.of(
-            Settings.GERRIT_DEFAULT_MESSAGE_DONE,
-            localizer.getText("message.empty.review")
-        );
-        commentMap = commentData.getCommentMap();
-        patchSetCommentMap = commentData.getPatchSetCommentMap();
-        patchSetComments = retrievePatchSetComments(gerritClientData);
-        revisionBase = gerritClientData.getOneBasedRevisionBase();
-        patchSetCommentAdded = new HashSet<>();
+  public AIChatHistory(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    super(config, changeSetData, localizer);
+    CommentData commentData = gerritClientData.getCommentData();
+    messagesExcludedFromHistory =
+        Set.of(Settings.GERRIT_DEFAULT_MESSAGE_DONE, localizer.getText("message.empty.review"));
+    commentMap = commentData.getCommentMap();
+    patchSetCommentMap = commentData.getPatchSetCommentMap();
+    patchSetComments = retrievePatchSetComments(gerritClientData);
+    revisionBase = gerritClientData.getOneBasedRevisionBase();
+    patchSetCommentAdded = new HashSet<>();
+  }
+
+  public List<AIChatRequestMessage> retrieveHistory(
+      GerritComment commentProperty, boolean filterActive) {
+    this.filterActive = filterActive;
+    if (commentProperty.isPatchSetComment()) {
+      return retrievePatchSetMessageHistory();
+    } else {
+      return retrieveMessageHistory(commentProperty);
     }
+  }
 
-    public List<AIChatRequestMessage> retrieveHistory(GerritComment commentProperty, boolean filterActive) {
-        this.filterActive = filterActive;
-        if (commentProperty.isPatchSetComment()) {
-            return retrievePatchSetMessageHistory();
-        }
-        else {
-            return retrieveMessageHistory(commentProperty);
-        }
-    }
+  public List<AIChatRequestMessage> retrieveHistory(GerritComment commentProperty) {
+    return retrieveHistory(commentProperty, false);
+  }
 
-    public List<AIChatRequestMessage> retrieveHistory(GerritComment commentProperty) {
-        return retrieveHistory(commentProperty, false);
-    }
+  private List<GerritComment> retrievePatchSetComments(GerritClientData gerritClientData) {
+    List<GerritComment> detailComments = gerritClientData.getDetailComments();
+    // Normalize detailComments by setting the `update` field to match `date`
+    detailComments.forEach(record -> record.setUpdated(record.getDate()));
+    // Join the comments from patchSetCommentMap with detailComments
+    List<GerritComment> patchSetComments =
+        Stream.concat(patchSetCommentMap.values().stream(), detailComments.stream())
+            .collect(Collectors.toList());
+    sortPatchSetComments(patchSetComments);
+    log.debug("Patch Set Comments sorted by `update` datetime: {}", patchSetComments);
 
-    private List<GerritComment> retrievePatchSetComments(GerritClientData gerritClientData) {
-        List<GerritComment> detailComments = gerritClientData.getDetailComments();
-        // Normalize detailComments by setting the `update` field to match `date`
-        detailComments.forEach(record -> record.setUpdated(record.getDate()));
-        // Join the comments from patchSetCommentMap with detailComments
-        List<GerritComment> patchSetComments = Stream.concat(patchSetCommentMap.values().stream(),
-                        detailComments.stream())
-                .collect(Collectors.toList());
-        sortPatchSetComments(patchSetComments);
-        log.debug("Patch Set Comments sorted by `update` datetime: {}", patchSetComments);
+    return patchSetComments;
+  }
 
-        return patchSetComments;
-    }
+  private void sortPatchSetComments(List<GerritComment> patchSetComments) {
+    Comparator<GerritComment> byDateUpdated =
+        (GerritComment o1, GerritComment o2) -> {
+          String dateTime1 = o1.getUpdated();
+          String dateTime2 = o2.getUpdated();
+          if (dateTime1 == null && dateTime2 == null) return 0;
+          if (dateTime1 == null) return 1;
+          if (dateTime2 == null) return -1;
 
-    private void sortPatchSetComments(List<GerritComment> patchSetComments) {
-        Comparator<GerritComment> byDateUpdated = (GerritComment o1, GerritComment o2) -> {
-            String dateTime1 = o1.getUpdated();
-            String dateTime2 = o2.getUpdated();
-            if (dateTime1 == null && dateTime2 == null) return 0;
-            if (dateTime1 == null) return 1;
-            if (dateTime2 == null) return -1;
-
-            return dateTime1.compareTo(dateTime2);
+          return dateTime1.compareTo(dateTime2);
         };
-        patchSetComments.sort(byDateUpdated);
-    }
+    patchSetComments.sort(byDateUpdated);
+  }
 
-    private String getRoleFromComment(GerritComment currentComment) {
-        return isFromAssistant(currentComment) ? Settings.OPEN_AI_CHAT_ROLE_ASSISTANT : Settings.OPEN_AI_CHAT_ROLE_USER;
-    }
+  private String getRoleFromComment(GerritComment currentComment) {
+    return isFromAssistant(currentComment)
+        ? Settings.OPEN_AI_CHAT_ROLE_ASSISTANT
+        : Settings.OPEN_AI_CHAT_ROLE_USER;
+  }
 
-    private List<AIChatRequestMessage> retrieveMessageHistory(GerritComment currentComment) {
-        List<AIChatRequestMessage> messageHistory = new ArrayList<>();
-        while (currentComment != null) {
-            addMessageToHistory(messageHistory, currentComment);
-            currentComment = commentMap.get(currentComment.getInReplyTo());
+  private List<AIChatRequestMessage> retrieveMessageHistory(GerritComment currentComment) {
+    List<AIChatRequestMessage> messageHistory = new ArrayList<>();
+    while (currentComment != null) {
+      addMessageToHistory(messageHistory, currentComment);
+      currentComment = commentMap.get(currentComment.getInReplyTo());
+    }
+    // Reverse the history sequence so that the oldest message appears first and the newest message
+    // is last
+    Collections.reverse(messageHistory);
+
+    return messageHistory;
+  }
+
+  private List<AIChatRequestMessage> retrievePatchSetMessageHistory() {
+    List<AIChatRequestMessage> messageHistory = new ArrayList<>();
+    for (GerritComment patchSetComment : patchSetComments) {
+      if (patchSetComment.isAutogenerated()) {
+        continue;
+      }
+      if (!isFromAssistant(patchSetComment)) {
+        GerritComment patchSetLevelMessage = patchSetCommentMap.get(patchSetComment.getId());
+        if (patchSetLevelMessage != null) {
+          patchSetComment = patchSetLevelMessage;
         }
-        // Reverse the history sequence so that the oldest message appears first and the newest message is last
-        Collections.reverse(messageHistory);
-
-        return messageHistory;
+      }
+      addMessageToHistory(messageHistory, patchSetComment);
     }
+    return messageHistory;
+  }
 
-    private List<AIChatRequestMessage> retrievePatchSetMessageHistory() {
-        List<AIChatRequestMessage> messageHistory = new ArrayList<>();
-        for (GerritComment patchSetComment : patchSetComments) {
-            if (patchSetComment.isAutogenerated()) {
-                continue;
-            }
-            if (!isFromAssistant(patchSetComment)) {
-                GerritComment patchSetLevelMessage = patchSetCommentMap.get(patchSetComment.getId());
-                if (patchSetLevelMessage != null) {
-                    patchSetComment = patchSetLevelMessage;
-                }
-            }
-            addMessageToHistory(messageHistory, patchSetComment);
-        }
-        return messageHistory;
+  private boolean isInactiveComment(GerritComment comment) {
+    return config.getIgnoreResolvedAIChatComments()
+            && isFromAssistant(comment)
+            && comment.isResolved()
+        || config.getIgnoreOutdatedInlineComments()
+            && comment.getOneBasedPatchSet() != revisionBase
+            && !comment.isPatchSetComment();
+  }
+
+  private void addMessageToHistory(
+      List<AIChatRequestMessage> messageHistory, GerritComment comment) {
+    String messageContent = getCleanedMessage(comment);
+    boolean shouldNotProcessComment =
+        messageContent.isEmpty()
+            || messagesExcludedFromHistory.contains(messageContent)
+            || patchSetCommentAdded.contains(messageContent)
+            || filterActive && isInactiveComment(comment);
+
+    if (shouldNotProcessComment && !commentMessage.isContainingHistoryCommand()) {
+      return;
     }
-
-    private boolean isInactiveComment(GerritComment comment) {
-        return config.getIgnoreResolvedAIChatComments() && isFromAssistant(comment) && comment.isResolved() ||
-                config.getIgnoreOutdatedInlineComments() && comment.getOneBasedPatchSet() != revisionBase &&
-                        !comment.isPatchSetComment();
+    patchSetCommentAdded.add(messageContent);
+    if (commentMessage.isContainingHistoryCommand()) {
+      commentMessage.processHistoryCommand();
+      return;
     }
-
-    private void addMessageToHistory(List<AIChatRequestMessage> messageHistory, GerritComment comment) {
-        String messageContent = getCleanedMessage(comment);
-        boolean shouldNotProcessComment = messageContent.isEmpty() ||
-                messagesExcludedFromHistory.contains(messageContent) ||
-                patchSetCommentAdded.contains(messageContent) ||
-                filterActive && isInactiveComment(comment);
-
-        if (shouldNotProcessComment && !commentMessage.isContainingHistoryCommand()) {
-            return;
-        }
-        patchSetCommentAdded.add(messageContent);
-        if (commentMessage.isContainingHistoryCommand()) {
-            commentMessage.processHistoryCommand();
-            return;
-        }
-        AIChatRequestMessage message = AIChatRequestMessage.builder()
-                .role(getRoleFromComment(comment))
-                .content(messageContent)
-                .build();
-        messageHistory.add(message);
-    }
+    AIChatRequestMessage message =
+        AIChatRequestMessage.builder()
+            .role(getRoleFromComment(comment))
+            .content(messageContent)
+            .build();
+    messageHistory.add(message);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatPromptFactory.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatPromptFactory.java
index 1d294a0..31a9d16 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatPromptFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/AIChatPromptFactory.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -17,38 +31,36 @@
 @Slf4j
 public class AIChatPromptFactory {
 
-    public static ChatGptPromptStateful getAIChatPromptStateful(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change
-    ) {
-        if (change.getIsCommentEvent()) {
-            log.info("AIChatPromptFactory: Returned AIChatGptPromptStatefulRequests");
-            return new AIChatGptPromptStatefulRequests(config, changeSetData, change);
-        } else {
-            log.info("AIChatPromptFactory: Returned AIChatGptPromptStatefulReview");
-            return new AIChatGptPromptStatefulReview(config, changeSetData, change);
-        }
+  public static ChatGptPromptStateful getAIChatPromptStateful(
+      Configuration config, ChangeSetData changeSetData, GerritChange change) {
+    if (change.getIsCommentEvent()) {
+      log.info("AIChatPromptFactory: Returned AIChatGptPromptStatefulRequests");
+      return new AIChatGptPromptStatefulRequests(config, changeSetData, change);
+    } else {
+      log.info("AIChatPromptFactory: Returned AIChatGptPromptStatefulReview");
+      return new AIChatGptPromptStatefulReview(config, changeSetData, change);
     }
+  }
 
-    public static ChatAIDataPrompt getChatGptDataPrompt(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        if (change.getIsCommentEvent()) {
-            if ((config.getAIMode() == Settings.Modes.stateless)) {
-                log.info("AIChatPromptFactory: Returned AIChatDataPromptRequestsStateless");
-                return new AIChatDataPromptRequestsStateless(config, changeSetData, gerritClientData, localizer);
-            } else {
-                log.info("AIChatPromptFactory: Returned AIChatDataPromptRequestsStateful");
-                return new AIChatDataPromptRequestsStateful(config, changeSetData, gerritClientData, localizer);
-            }
-        } else {
-            log.info("AIChatPromptFactory: Returned AIChatDataPromptReview");
-            return new AIChatDataPromptReview(config, changeSetData, gerritClientData, localizer);
-        }
+  public static ChatAIDataPrompt getChatGptDataPrompt(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    if (change.getIsCommentEvent()) {
+      if ((config.getAIMode() == Settings.Modes.stateless)) {
+        log.info("AIChatPromptFactory: Returned AIChatDataPromptRequestsStateless");
+        return new AIChatDataPromptRequestsStateless(
+            config, changeSetData, gerritClientData, localizer);
+      } else {
+        log.info("AIChatPromptFactory: Returned AIChatDataPromptRequestsStateful");
+        return new AIChatDataPromptRequestsStateful(
+            config, changeSetData, gerritClientData, localizer);
+      }
+    } else {
+      log.info("AIChatPromptFactory: Returned AIChatDataPromptReview");
+      return new AIChatDataPromptReview(config, changeSetData, gerritClientData, localizer);
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/Directives.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/Directives.java
index ea1a6ed..5330e41 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/Directives.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/Directives.java
@@ -1,22 +1,36 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 
 public class Directives {
-    private final ChangeSetData changeSetData;
-    private String directive;
+  private final ChangeSetData changeSetData;
+  private String directive;
 
-    public Directives(ChangeSetData changeSetData) {
-        this.changeSetData = changeSetData;
-    }
+  public Directives(ChangeSetData changeSetData) {
+    this.changeSetData = changeSetData;
+  }
 
-    public void addDirective(String directive) {
-        this.directive = directive.trim();
-    }
+  public void addDirective(String directive) {
+    this.directive = directive.trim();
+  }
 
-    public void copyDirectiveToSettings() {
-        if (directive != null && !directive.isEmpty()) {
-            changeSetData.getDirectives().add(directive);
-        }
+  public void copyDirectiveToSettings() {
+    if (directive != null && !directive.isEmpty()) {
+      changeSetData.getDirectives().add(directive);
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/MessageSanitizer.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/MessageSanitizer.java
index 7bf49d2..64c5d00 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/MessageSanitizer.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/client/prompt/MessageSanitizer.java
@@ -1,44 +1,73 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.StringUtils.backslashEachChar;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.CODE_DELIMITER;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.CODE_DELIMITER_BEGIN;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.CODE_DELIMITER_END;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.INLINE_CODE_DELIMITER;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.parseOutOfDelimiters;
+
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
-
 public class MessageSanitizer {
-    private static final Pattern SANITIZE_BOLD_REGEX = Pattern.compile("(\\*{1,2}|(?<!\\w)_{1,2})(.+?)\\1",
-            Pattern.DOTALL);
-    private static final Pattern SANITIZE_NUM_REGEX = Pattern.compile("^(\\s*)(#+)(?=\\s)", Pattern.MULTILINE);
+  private static final Pattern SANITIZE_BOLD_REGEX =
+      Pattern.compile("(\\*{1,2}|(?<!\\w)_{1,2})(.+?)\\1", Pattern.DOTALL);
+  private static final Pattern SANITIZE_NUM_REGEX =
+      Pattern.compile("^(\\s*)(#+)(?=\\s)", Pattern.MULTILINE);
 
-    public static String sanitizeAIChatMessage(String message) {
-        // Sanitize code blocks (delimited by CODE_DELIMITER) by stripping out the language for syntax highlighting and
-        // ensuring that is preceded by two "\n" chars. Additionally, sanitize the content outside these blocks.
-        return parseOutOfDelimiters(message, "\\s*" + CODE_DELIMITER + "\\w*\\s*",
-                MessageSanitizer::sanitizeOutsideInlineCodeBlocks, CODE_DELIMITER_BEGIN, CODE_DELIMITER_END);
+  public static String sanitizeAIChatMessage(String message) {
+    // Sanitize code blocks (delimited by CODE_DELIMITER) by stripping out the language for syntax
+    // highlighting and
+    // ensuring that is preceded by two "\n" chars. Additionally, sanitize the content outside these
+    // blocks.
+    return parseOutOfDelimiters(
+        message,
+        "\\s*" + CODE_DELIMITER + "\\w*\\s*",
+        MessageSanitizer::sanitizeOutsideInlineCodeBlocks,
+        CODE_DELIMITER_BEGIN,
+        CODE_DELIMITER_END);
+  }
+
+  private static String sanitizeOutsideInlineCodeBlocks(String message) {
+    // Sanitize the content outside the inline code blocks (delimited by INLINE_CODE_DELIMITER).
+    return parseOutOfDelimiters(
+        message, INLINE_CODE_DELIMITER, MessageSanitizer::sanitizeGerritComment);
+  }
+
+  private static String sanitizeGerritComment(String message) {
+    // Sanitize sequences of asterisks ("*") and underscores ("_") that would be incorrectly
+    // interpreted as
+    // delimiters of Italic and Bold text.
+    Matcher boldSanitizeMatcher = SANITIZE_BOLD_REGEX.matcher(message);
+    StringBuilder result = new StringBuilder();
+    while (boldSanitizeMatcher.find()) {
+      String slashedDelimiter = backslashEachChar(boldSanitizeMatcher.group(1));
+      boldSanitizeMatcher.appendReplacement(
+          result, slashedDelimiter + boldSanitizeMatcher.group(2) + slashedDelimiter);
     }
+    boldSanitizeMatcher.appendTail(result);
+    message = result.toString();
 
-    private static String sanitizeOutsideInlineCodeBlocks(String message) {
-        // Sanitize the content outside the inline code blocks (delimited by INLINE_CODE_DELIMITER).
-        return parseOutOfDelimiters(message, INLINE_CODE_DELIMITER, MessageSanitizer::sanitizeGerritComment);
-    }
+    // Sanitize sequences of number signs ("#") that would be incorrectly interpreted as header
+    // prefixes.
+    Matcher numSanitizeMatcher = SANITIZE_NUM_REGEX.matcher(message);
+    message = numSanitizeMatcher.replaceAll("$1\\\\$2");
 
-    private static String sanitizeGerritComment(String message) {
-        // Sanitize sequences of asterisks ("*") and underscores ("_") that would be incorrectly interpreted as
-        // delimiters of Italic and Bold text.
-        Matcher boldSanitizeMatcher = SANITIZE_BOLD_REGEX.matcher(message);
-        StringBuilder result = new StringBuilder();
-        while (boldSanitizeMatcher.find()) {
-            String slashedDelimiter = backslashEachChar(boldSanitizeMatcher.group(1));
-            boldSanitizeMatcher.appendReplacement(result, slashedDelimiter + boldSanitizeMatcher.group(2) +
-                    slashedDelimiter);
-        }
-        boldSanitizeMatcher.appendTail(result);
-        message = result.toString();
-
-        // Sanitize sequences of number signs ("#") that would be incorrectly interpreted as header prefixes.
-        Matcher numSanitizeMatcher = SANITIZE_NUM_REGEX.matcher(message);
-        message = numSanitizeMatcher.replaceAll("$1\\\\$2");
-
-        return message;
-    }
+    return message;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritCodeRange.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritCodeRange.java
index 6917006..b7f5de3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritCodeRange.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritCodeRange.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import com.google.gson.annotations.SerializedName;
@@ -7,12 +21,15 @@
 @Data
 @Builder(toBuilder = true)
 public class GerritCodeRange {
-    @SerializedName("start_line")
-    public int startLine;
-    @SerializedName("end_line")
-    public int endLine;
-    @SerializedName("start_character")
-    public int startCharacter;
-    @SerializedName("end_character")
-    public int endCharacter;
+  @SerializedName("start_line")
+  public int startLine;
+
+  @SerializedName("end_line")
+  public int endLine;
+
+  @SerializedName("start_character")
+  public int startCharacter;
+
+  @SerializedName("end_character")
+  public int endCharacter;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritComment.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritComment.java
index defea13..6b3fb93 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritComment.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import com.google.gson.annotations.SerializedName;
@@ -6,52 +20,63 @@
 
 @Data
 public class GerritComment {
-    private Author author;
-    @SerializedName("change_message_id")
-    private String changeMessageId;
-    private Boolean unresolved;
-    @SerializedName("patch_set")
-    private Integer patchSet;
-    private String id;
-    private String tag;
-    private Integer line;
-    private GerritCodeRange range;
-    @SerializedName("in_reply_to")
-    private String inReplyTo;
-    private String updated;
-    // Field `date` is used by messages from PatchSet details
-    private String date;
-    private String message;
-    @SerializedName("commit_id")
-    private String commitId;
-    // Metadata field that is set to the commented filename
-    private String filename;
+  private Author author;
 
-    @Data
-    public static class Author {
-        @SerializedName("_account_id")
-        private int accountId;
-        private String name;
-        @SerializedName("display_name")
-        private String displayName;
-        private String email;
-        private String username;
-    }
+  @SerializedName("change_message_id")
+  private String changeMessageId;
 
-    public boolean isAutogenerated() {
-        return tag != null && tag.startsWith(Settings.GERRIT_AUTOGENERATED_PREFIX);
-    }
+  private Boolean unresolved;
 
-    public boolean isPatchSetComment() {
-        return filename != null && filename.equals(Settings.GERRIT_PATCH_SET_FILENAME);
-    }
+  @SerializedName("patch_set")
+  private Integer patchSet;
 
-    public boolean isResolved() {
-        // unresolved is null for the messages from Patch Set details
-        return unresolved != null && !unresolved;
-    }
+  private String id;
+  private String tag;
+  private Integer line;
+  private GerritCodeRange range;
 
-    public int getOneBasedPatchSet() {
-        return patchSet == null ? 1 : Math.max(patchSet, 1);
-    }
+  @SerializedName("in_reply_to")
+  private String inReplyTo;
+
+  private String updated;
+  // Field `date` is used by messages from PatchSet details
+  private String date;
+  private String message;
+
+  @SerializedName("commit_id")
+  private String commitId;
+
+  // Metadata field that is set to the commented filename
+  private String filename;
+
+  @Data
+  public static class Author {
+    @SerializedName("_account_id")
+    private int accountId;
+
+    private String name;
+
+    @SerializedName("display_name")
+    private String displayName;
+
+    private String email;
+    private String username;
+  }
+
+  public boolean isAutogenerated() {
+    return tag != null && tag.startsWith(Settings.GERRIT_AUTOGENERATED_PREFIX);
+  }
+
+  public boolean isPatchSetComment() {
+    return filename != null && filename.equals(Settings.GERRIT_PATCH_SET_FILENAME);
+  }
+
+  public boolean isResolved() {
+    // unresolved is null for the messages from Patch Set details
+    return unresolved != null && !unresolved;
+  }
+
+  public int getOneBasedPatchSet() {
+    return patchSet == null ? 1 : Math.max(patchSet, 1);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritFileDiff.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritFileDiff.java
index 4daf319..148b142 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritFileDiff.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritFileDiff.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import com.google.gson.annotations.SerializedName;
@@ -5,15 +19,17 @@
 
 @Data
 public abstract class GerritFileDiff {
-    @SerializedName("meta_a")
-    protected Meta metaA;
-    @SerializedName("meta_b")
-    protected Meta metaB;
+  @SerializedName("meta_a")
+  protected Meta metaA;
 
-    @Data
-    public static class Meta {
-        String name;
-        @SerializedName("content_type")
-        String contentType;
-    }
+  @SerializedName("meta_b")
+  protected Meta metaB;
+
+  @Data
+  public static class Meta {
+    String name;
+
+    @SerializedName("content_type")
+    String contentType;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetDetail.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetDetail.java
index d33a02d..5d56599 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetDetail.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetDetail.java
@@ -1,36 +1,53 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import com.google.gson.annotations.SerializedName;
+import java.util.List;
 import lombok.Data;
 
-import java.util.List;
-
-// TODO remove once migration to GerritApi is finished and com.google.gerrit.extensions.common.ChangeInfo is used
+// TODO remove once migration to GerritApi is finished and
+// com.google.gerrit.extensions.common.ChangeInfo is used
 @Data
 public class GerritPatchSetDetail {
-    private Labels labels;
-    private List<GerritComment> messages;
-    @SerializedName("work_in_progress")
-    private Boolean workInProgress;
+  private Labels labels;
+  private List<GerritComment> messages;
 
-    @Data
-    public static class Labels {
-        @SerializedName("Code-Review")
-        private CodeReview codeReview;
-    }
+  @SerializedName("work_in_progress")
+  private Boolean workInProgress;
 
-    @Data
-    public static class CodeReview {
-        private List<Permission> all;
-    }
+  @Data
+  public static class Labels {
+    @SerializedName("Code-Review")
+    private CodeReview codeReview;
+  }
 
-    @Data
-    public static class Permission {
-        private Integer value;
-        private String date;
-        @SerializedName("permitted_voting_range")
-        private GerritPermittedVotingRange permittedVotingRange;
-        @SerializedName("_account_id")
-        private int accountId;
-    }
+  @Data
+  public static class CodeReview {
+    private List<Permission> all;
+  }
+
+  @Data
+  public static class Permission {
+    private Integer value;
+    private String date;
+
+    @SerializedName("permitted_voting_range")
+    private GerritPermittedVotingRange permittedVotingRange;
+
+    @SerializedName("_account_id")
+    private int accountId;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetFileDiff.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetFileDiff.java
index 27d4873..88f9e91 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetFileDiff.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPatchSetFileDiff.java
@@ -1,19 +1,32 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
+import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
-import java.util.List;
-
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class GerritPatchSetFileDiff extends GerritFileDiff {
-    private List<Content> content;
+  private List<Content> content;
 
-    @Data
-    public static class Content {
-        public List<String> a;
-        public List<String> b;
-        public List<String> ab;
-    }
+  @Data
+  public static class Content {
+    public List<String> a;
+    public List<String> b;
+    public List<String> ab;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPermittedVotingRange.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPermittedVotingRange.java
index 1220941..e266647 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPermittedVotingRange.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritPermittedVotingRange.java
@@ -1,9 +1,23 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import lombok.Data;
 
 @Data
 public class GerritPermittedVotingRange {
-    private int min;
-    private int max;
+  private int min;
+  private int max;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReview.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReview.java
index 8a6bfb9..4ffc969 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReview.java
@@ -1,22 +1,35 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.Map;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 
-import java.util.List;
-import java.util.Map;
-
 @Data
 public class GerritReview {
-    private Map<String, List<GerritComment>> comments;
-    private String message;
-    private Labels labels;
+  private Map<String, List<GerritComment>> comments;
+  private String message;
+  private Labels labels;
 
-    @AllArgsConstructor
-    @Data
-    public static class Labels {
-        @SerializedName("Code-Review")
-        private int codeReview;
-    }
+  @AllArgsConstructor
+  @Data
+  public static class Labels {
+    @SerializedName("Code-Review")
+    private int codeReview;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReviewFileDiff.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReviewFileDiff.java
index 7e8c81b..1425bf2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReviewFileDiff.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/gerrit/GerritReviewFileDiff.java
@@ -1,18 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.diff.DiffContent;
+import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
-import java.util.List;
-
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class GerritReviewFileDiff extends GerritFileDiff {
-    private List<DiffContent> content;
+  private List<DiffContent> content;
 
-    public GerritReviewFileDiff(Meta metaA, Meta metaB) {
-        this.metaA = metaA;
-        this.metaB = metaB;
-    }
+  public GerritReviewFileDiff(Meta metaA, Meta metaB) {
+    this.metaA = metaA;
+    this.metaB = metaB;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatDialogueItem.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatDialogueItem.java
index 338a70f..0a299c4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatDialogueItem.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatDialogueItem.java
@@ -1,11 +1,25 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import lombok.Data;
 
 @Data
 public abstract class AIChatDialogueItem {
-    protected Integer id;
-    protected String filename;
-    protected Integer lineNumber;
-    protected String codeSnippet;
+  protected Integer id;
+  protected String filename;
+  protected Integer lineNumber;
+  protected String codeSnippet;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatMessageItem.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatMessageItem.java
index 031bc73..259bc7a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatMessageItem.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatMessageItem.java
@@ -1,13 +1,26 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
+import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
-import java.util.List;
-
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class AIChatMessageItem extends AIChatDialogueItem {
-    private String request;
-    private List<AIChatRequestMessage> history;
+  private String request;
+  private List<AIChatRequestMessage> history;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatReplyItem.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatReplyItem.java
index eef7929..98fc9de 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatReplyItem.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatReplyItem.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import lombok.Data;
@@ -6,9 +20,9 @@
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class AIChatReplyItem extends AIChatDialogueItem {
-    private String reply;
-    private Integer score;
-    private Double relevance;
-    private boolean repeated;
-    private boolean conflicting;
+  private String reply;
+  private Integer score;
+  private Double relevance;
+  private boolean repeated;
+  private boolean conflicting;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatRequestMessage.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatRequestMessage.java
index f476d15..614dc50 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatRequestMessage.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatRequestMessage.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import lombok.Builder;
@@ -6,8 +20,8 @@
 @Data
 @Builder
 public class AIChatRequestMessage {
-    private String role;
-    private String content;
-    // PatchSet changeId passed in the request
-    private String changeId;
+  private String role;
+  private String content;
+  // PatchSet changeId passed in the request
+  private String changeId;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseContent.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseContent.java
index 90a425e..b2df1d5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseContent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseContent.java
@@ -1,16 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
+import java.util.List;
 import lombok.Data;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 
-import java.util.List;
-
 @Data
 @RequiredArgsConstructor
 public class AIChatResponseContent {
-    private List<AIChatReplyItem> replies;
-    private String changeId;
-    @NonNull
-    private String messageContent;
+  private List<AIChatReplyItem> replies;
+  private String changeId;
+  @NonNull private String messageContent;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseMessage.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseMessage.java
index f2c85f9..2cbefb0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseMessage.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseMessage.java
@@ -1,22 +1,37 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import com.google.gson.annotations.SerializedName;
-import lombok.Data;
-
 import java.util.List;
+import lombok.Data;
 
 @Data
 public class AIChatResponseMessage {
-    private String role;
-    private String type;
-    @SerializedName("tool_calls")
-    private List<AIChatToolCall> toolCalls;
-    @SerializedName("message_creation")
-    private MessageCreation messageCreation;
+  private String role;
+  private String type;
 
-    @Data
-    public static class MessageCreation {
-        @SerializedName("message_id")
-        private String messageId;
-    }
+  @SerializedName("tool_calls")
+  private List<AIChatToolCall> toolCalls;
+
+  @SerializedName("message_creation")
+  private MessageCreation messageCreation;
+
+  @Data
+  public static class MessageCreation {
+    @SerializedName("message_id")
+    private String messageId;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseStreamed.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseStreamed.java
index 81960c3..e9c915e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseStreamed.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseStreamed.java
@@ -1,19 +1,33 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import com.google.gson.annotations.SerializedName;
-import lombok.Data;
-
 import java.util.List;
+import lombok.Data;
 
 @Data
 public class AIChatResponseStreamed {
-    private List<Choice> choices;
+  private List<Choice> choices;
 
-    @Data
-    public static class Choice {
-        protected AIChatResponseMessage delta;
-        protected int index;
-        @SerializedName("finish_reason")
-        protected String finishReason;
-    }
+  @Data
+  public static class Choice {
+    protected AIChatResponseMessage delta;
+    protected int index;
+
+    @SerializedName("finish_reason")
+    protected String finishReason;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseUnstreamed.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseUnstreamed.java
index bc590e2..98266c6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseUnstreamed.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatResponseUnstreamed.java
@@ -1,15 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
-import lombok.Data;
-
 import java.util.List;
+import lombok.Data;
 
 @Data
 public class AIChatResponseUnstreamed {
-    private List<MessageChoice> choices;
+  private List<MessageChoice> choices;
 
-    @Data
-    public static class MessageChoice {
-        private AIChatResponseMessage message;
-    }
+  @Data
+  public static class MessageChoice {
+    private AIChatResponseMessage message;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatTool.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatTool.java
index 176e29c..9611417 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatTool.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatTool.java
@@ -1,67 +1,80 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
+import java.util.List;
 import lombok.Data;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 
-import java.util.List;
-
 @RequiredArgsConstructor
 @Data
 public class AIChatTool {
-    @NonNull
-    private String type;
-    private Function function;
+  @NonNull private String type;
+  private Function function;
+
+  @Data
+  public static class Function {
+    private String name;
+    private String description;
+    private Parameters parameters;
 
     @Data
-    public static class Function {
-        private String name;
-        private String description;
-        private Parameters parameters;
+    public static class Parameters {
+      private String type;
+      private Properties properties;
+      private List<String> required;
+
+      @Data
+      public static class Properties {
+        private Property replies;
+        // Field `changeId` expected in the response to correspond with the PatchSet changeId in the
+        // request
+        private Field changeId;
 
         @Data
-        public static class Parameters {
+        public static class Property {
+          private String type;
+          private Item items;
+
+          @Data
+          public static class Item {
             private String type;
-            private Properties properties;
+            private ObjectProperties properties;
             private List<String> required;
 
             @Data
-            public static class Properties {
-                private Property replies;
-                // Field `changeId` expected in the response to correspond with the PatchSet changeId in the request
-                private Field changeId;
-
-                @Data
-                public static class Property {
-                    private String type;
-                    private Item items;
-
-                    @Data
-                    public static class Item {
-                        private String type;
-                        private ObjectProperties properties;
-                        private List<String> required;
-
-                        @Data
-                        public static class ObjectProperties {
-                            private Field id;
-                            private Field reply;
-                            private Field score;
-                            private Field relevance;
-                            private Field repeated;
-                            private Field conflicting;
-                            private Field filename;
-                            private Field lineNumber;
-                            private Field codeSnippet;
-                        }
-                    }
-                }
-
-                @Data
-                public static class Field {
-                    private String type;
-                }
+            public static class ObjectProperties {
+              private Field id;
+              private Field reply;
+              private Field score;
+              private Field relevance;
+              private Field repeated;
+              private Field conflicting;
+              private Field filename;
+              private Field lineNumber;
+              private Field codeSnippet;
             }
+          }
         }
+
+        @Data
+        public static class Field {
+          private String type;
+        }
+      }
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolCall.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolCall.java
index 605015f..2abe200 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolCall.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolCall.java
@@ -1,16 +1,30 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import lombok.Data;
 
 @Data
 public class AIChatToolCall {
-    private String id;
-    private String type;
-    private Function function;
+  private String id;
+  private String type;
+  private Function function;
 
-    @Data
-    public static class Function {
-        private String name;
-        private String arguments;
-    }
+  @Data
+  public static class Function {
+    private String name;
+    private String arguments;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolChoice.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolChoice.java
index 1ba0478..9d49dd8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolChoice.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/api/openai/AIChatToolChoice.java
@@ -1,14 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai;
 
 import lombok.Data;
 
 @Data
 public class AIChatToolChoice {
-    private String type;
-    private Function function;
+  private String type;
+  private Function function;
 
-    @Data
-    public static class Function {
-        private String name;
-    }
+  @Data
+  public static class Function {
+    private String name;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/ChangeSetData.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/ChangeSetData.java
index 5b5fa4f..195bb58 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/ChangeSetData.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/ChangeSetData.java
@@ -1,40 +1,50 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data;
 
+import java.util.HashSet;
+import java.util.Set;
 import lombok.Data;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.HashSet;
-import java.util.Set;
-
 @RequiredArgsConstructor
 @Data
 @Slf4j
 public class ChangeSetData {
-    @NonNull
-    private Integer gptAccountId;
-    private String reviewAIDataPrompt;
-    private Integer commentPropertiesSize;
-    @NonNull
-    private Integer votingMinScore;
-    @NonNull
-    private Integer votingMaxScore;
+  @NonNull private Integer gptAccountId;
+  private String reviewAIDataPrompt;
+  private Integer commentPropertiesSize;
+  @NonNull private Integer votingMinScore;
+  @NonNull private Integer votingMaxScore;
 
-    // Command variables
-    private Boolean forcedReview = false;
-    private Boolean forcedReviewLastPatchSet = false;
-    private Boolean replyFilterEnabled = true;
-    private Boolean debugReviewMode = false;
-    private Boolean hideAICodeReview = false;
-    private Set<String> directives = new HashSet<>();
-    private String reviewSystemMessage;
+  // Command variables
+  private Boolean forcedReview = false;
+  private Boolean forcedReviewLastPatchSet = false;
+  private Boolean replyFilterEnabled = true;
+  private Boolean debugReviewMode = false;
+  private Boolean hideAICodeReview = false;
+  private Set<String> directives = new HashSet<>();
+  private String reviewSystemMessage;
 
-    public Boolean shouldHideAICodeReview() {
-        return hideAICodeReview && !forcedReview;
-    }
+  public Boolean shouldHideAICodeReview() {
+    return hideAICodeReview && !forcedReview;
+  }
 
-    public Boolean shouldRequestAICodeReview() {
-        return reviewSystemMessage == null && !shouldHideAICodeReview();
-    }
+  public Boolean shouldRequestAICodeReview() {
+    return reviewSystemMessage == null && !shouldHideAICodeReview();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/CommentData.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/CommentData.java
index b7085ab..ce64132 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/CommentData.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/CommentData.java
@@ -1,16 +1,29 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
 import java.util.HashMap;
 import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
 
 @AllArgsConstructor
 @Data
 public class CommentData {
-    private List<GerritComment> commentProperties;
-    private HashMap<String, GerritComment> commentMap;
-    private HashMap<String, GerritComment> patchSetCommentMap;
+  private List<GerritComment> commentProperties;
+  private HashMap<String, GerritComment> commentMap;
+  private HashMap<String, GerritComment> patchSetCommentMap;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/GerritClientData.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/GerritClientData.java
index 8dfa887..c73da04 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/GerritClientData.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/data/GerritClientData.java
@@ -1,26 +1,39 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.patch.diff.FileDiffProcessed;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritComment;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
 import java.util.HashMap;
 import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
 
 @AllArgsConstructor
 @Data
 public class GerritClientData {
-    private HashMap<String, FileDiffProcessed> fileDiffsProcessed;
-    private List<GerritComment> detailComments;
-    private CommentData commentData;
-    private Integer revisionBase;
+  private HashMap<String, FileDiffProcessed> fileDiffsProcessed;
+  private List<GerritComment> detailComments;
+  private CommentData commentData;
+  private Integer revisionBase;
 
-    public List<GerritComment> getCommentProperties() {
-        return commentData.getCommentProperties();
-    }
+  public List<GerritComment> getCommentProperties() {
+    return commentData.getCommentProperties();
+  }
 
-    public int getOneBasedRevisionBase() {
-        return revisionBase +1;
-    }
+  public int getOneBasedRevisionBase() {
+    return revisionBase + 1;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/code/CodeFinderDiff.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/code/CodeFinderDiff.java
index cd826d1..71f9edf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/code/CodeFinderDiff.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/code/CodeFinderDiff.java
@@ -1,14 +1,27 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.code;
 
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.diff.DiffContent;
+import java.util.TreeMap;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 
-import java.util.TreeMap;
-
 @AllArgsConstructor
 @Data
 public class CodeFinderDiff {
-    private DiffContent content;
-    private TreeMap<Integer, Integer> charToLineMap;
+  private DiffContent content;
+  private TreeMap<Integer, Integer> charToLineMap;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/diff/DiffContent.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/diff/DiffContent.java
index 620acc9..dbb39a7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/diff/DiffContent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/patch/diff/DiffContent.java
@@ -1,10 +1,24 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.patch.diff;
 
 import lombok.Data;
 
 @Data
 public class DiffContent {
-    public String a;
-    public String b;
-    public String ab;
+  public String a;
+  public String b;
+  public String ab;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/review/ReviewBatch.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/review/ReviewBatch.java
index cb1f356..e907ed6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/review/ReviewBatch.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/common/model/review/ReviewBatch.java
@@ -1,23 +1,36 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.common.model.review;
 
+import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_PATCH_SET_FILENAME;
+
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.gerrit.GerritCodeRange;
 import lombok.Data;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 
-import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_PATCH_SET_FILENAME;
-
 @Data
 @RequiredArgsConstructor
 public class ReviewBatch {
-    private String id;
-    @NonNull
-    private String content;
-    private String filename;
-    private Integer line;
-    private GerritCodeRange range;
+  private String id;
+  @NonNull private String content;
+  private String filename;
+  private Integer line;
+  private GerritCodeRange range;
 
-    public String getFilename() {
-        return filename == null ? GERRIT_PATCH_SET_FILENAME : filename;
-    }
+  public String getFilename() {
+    return filename == null ? GERRIT_PATCH_SET_FILENAME : filename;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/UriResourceLocatorStateful.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/UriResourceLocatorStateful.java
index 4525c25..cfefee3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/UriResourceLocatorStateful.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/UriResourceLocatorStateful.java
@@ -1,49 +1,63 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api;
 
 public class UriResourceLocatorStateful {
-    private static final String VERSION_URI = "/v1";
+  private static final String VERSION_URI = "/v1";
 
-    public static String filesCreateUri() {
-        return VERSION_URI + "/files";
-    }
+  public static String filesCreateUri() {
+    return VERSION_URI + "/files";
+  }
 
-    public static String assistantCreateUri() {
-        return VERSION_URI + "/assistants";
-    }
+  public static String assistantCreateUri() {
+    return VERSION_URI + "/assistants";
+  }
 
-    public static String threadsUri() {
-        return VERSION_URI + "/threads";
-    }
+  public static String threadsUri() {
+    return VERSION_URI + "/threads";
+  }
 
-    public static String threadRetrieveUri(String threadId) {
-        return threadsUri() + "/" + threadId;
-    }
+  public static String threadRetrieveUri(String threadId) {
+    return threadsUri() + "/" + threadId;
+  }
 
-    public static String threadMessagesUri(String threadId) {
-        return threadRetrieveUri(threadId) + "/messages";
-    }
+  public static String threadMessagesUri(String threadId) {
+    return threadRetrieveUri(threadId) + "/messages";
+  }
 
-    public static String threadMessageRetrieveUri(String threadId, String messageId) {
-        return threadMessagesUri(threadId) + "/" + messageId;
-    }
+  public static String threadMessageRetrieveUri(String threadId, String messageId) {
+    return threadMessagesUri(threadId) + "/" + messageId;
+  }
 
-    public static String runsUri(String threadId) {
-        return threadRetrieveUri(threadId) + "/runs";
-    }
+  public static String runsUri(String threadId) {
+    return threadRetrieveUri(threadId) + "/runs";
+  }
 
-    public static String runRetrieveUri(String threadId, String runId) {
-        return runsUri(threadId) + "/" + runId;
-    }
+  public static String runRetrieveUri(String threadId, String runId) {
+    return runsUri(threadId) + "/" + runId;
+  }
 
-    public static String runStepsUri(String threadId, String runId) {
-        return runRetrieveUri(threadId, runId) + "/steps";
-    }
+  public static String runStepsUri(String threadId, String runId) {
+    return runRetrieveUri(threadId, runId) + "/steps";
+  }
 
-    public static String runCancelUri(String threadId, String runId) {
-        return runRetrieveUri(threadId, runId) + "/cancel";
-    }
+  public static String runCancelUri(String threadId, String runId) {
+    return runRetrieveUri(threadId, runId) + "/cancel";
+  }
 
-    public static String vectorStoreCreateUri() {
-        return VERSION_URI + "/vector_stores";
-    }
+  public static String vectorStoreCreateUri() {
+    return VERSION_URI + "/vector_stores";
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatai/AIChatClientStateful.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatai/AIChatClientStateful.java
index 9c586fe..5ef8994 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatai/AIChatClientStateful.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatai/AIChatClientStateful.java
@@ -1,13 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatai;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.JsonTextUtils.isJsonString;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.JsonTextUtils.unwrapJsonCode;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.openapi.ChatAIClient;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseContent;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt.ChatGptRun;
@@ -17,93 +35,81 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptThreadMessageResponse;
 import lombok.extern.slf4j.Slf4j;
 
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.JsonTextUtils.isJsonString;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.JsonTextUtils.unwrapJsonCode;
-
 @Slf4j
 @Singleton
 public class AIChatClientStateful extends AIChatClient implements ChatAIClient {
-    private static final String TYPE_MESSAGE_CREATION = "message_creation";
-    private static final String TYPE_TOOL_CALLS = "tool_calls";
+  private static final String TYPE_MESSAGE_CREATION = "message_creation";
+  private static final String TYPE_TOOL_CALLS = "tool_calls";
 
-    private final GitRepoFiles gitRepoFiles;
-    private final PluginDataHandlerProvider pluginDataHandlerProvider;
+  private final GitRepoFiles gitRepoFiles;
+  private final PluginDataHandlerProvider pluginDataHandlerProvider;
 
-    @VisibleForTesting
-    @Inject
-    public AIChatClientStateful(
-            Configuration config,
-            GitRepoFiles gitRepoFiles,
-            PluginDataHandlerProvider pluginDataHandlerProvider
-    ) {
-        super(config);
-        this.gitRepoFiles = gitRepoFiles;
-        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  @VisibleForTesting
+  @Inject
+  public AIChatClientStateful(
+      Configuration config,
+      GitRepoFiles gitRepoFiles,
+      PluginDataHandlerProvider pluginDataHandlerProvider) {
+    super(config);
+    this.gitRepoFiles = gitRepoFiles;
+    this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  }
+
+  public AIChatResponseContent ask(
+      ChangeSetData changeSetData, GerritChange change, String patchSet) {
+    isCommentEvent = change.getIsCommentEvent();
+    String changeId = change.getFullChangeId();
+    log.info(
+        "Processing STATEFUL ChatGPT Request with changeId: {}, Patch Set: {}", changeId, patchSet);
+
+    ChatGptThread chatGptThread = new ChatGptThread(config, pluginDataHandlerProvider);
+    String threadId = chatGptThread.createThread();
+
+    ChatGptThreadMessage chatGptThreadMessage =
+        new ChatGptThreadMessage(threadId, config, changeSetData, change, patchSet);
+    chatGptThreadMessage.addMessage();
+
+    ChatGptRun chatGptRun =
+        new ChatGptRun(
+            threadId, config, changeSetData, change, gitRepoFiles, pluginDataHandlerProvider);
+    chatGptRun.createRun();
+    chatGptRun.pollRunStep();
+    // Attribute `requestBody` is valued for testing purposes
+    requestBody = chatGptThreadMessage.getAddMessageRequestBody();
+    log.debug("ChatGPT request body: {}", requestBody);
+
+    AIChatResponseContent AIChatResponseContent = getResponseContentStateful(threadId, chatGptRun);
+    chatGptRun.cancelRun();
+
+    return AIChatResponseContent;
+  }
+
+  private AIChatResponseContent getResponseContentStateful(String threadId, ChatGptRun chatGptRun) {
+    return switch (chatGptRun.getFirstStepDetails().getType()) {
+      case TYPE_MESSAGE_CREATION -> retrieveThreadMessage(threadId, chatGptRun);
+      case TYPE_TOOL_CALLS -> getResponseContent(chatGptRun.getFirstStepToolCalls());
+      default ->
+          throw new IllegalStateException(
+              "Unexpected Step Type in stateful ChatGpt response: " + chatGptRun);
+    };
+  }
+
+  private AIChatResponseContent retrieveThreadMessage(String threadId, ChatGptRun chatGptRun) {
+    ChatGptThreadMessage chatGptThreadMessage = new ChatGptThreadMessage(threadId, config);
+    ChatGptThreadMessageResponse threadMessageResponse =
+        chatGptThreadMessage.retrieveMessage(
+            chatGptRun.getFirstStepDetails().getMessageCreation().getMessageId());
+    String responseText = threadMessageResponse.getContent().get(0).getText().getValue();
+    if (responseText == null) {
+      throw new RuntimeException("ChatGPT thread message response content is null");
     }
-
-    public AIChatResponseContent ask(ChangeSetData changeSetData, GerritChange change, String patchSet) {
-        isCommentEvent = change.getIsCommentEvent();
-        String changeId = change.getFullChangeId();
-        log.info("Processing STATEFUL ChatGPT Request with changeId: {}, Patch Set: {}", changeId, patchSet);
-
-        ChatGptThread chatGptThread = new ChatGptThread(config, pluginDataHandlerProvider);
-        String threadId = chatGptThread.createThread();
-
-        ChatGptThreadMessage chatGptThreadMessage = new ChatGptThreadMessage(
-                threadId,
-                config,
-                changeSetData,
-                change,
-                patchSet
-        );
-        chatGptThreadMessage.addMessage();
-
-        ChatGptRun chatGptRun = new ChatGptRun(
-                threadId,
-                config,
-                changeSetData,
-                change,
-                gitRepoFiles,
-                pluginDataHandlerProvider
-        );
-        chatGptRun.createRun();
-        chatGptRun.pollRunStep();
-        // Attribute `requestBody` is valued for testing purposes
-        requestBody = chatGptThreadMessage.getAddMessageRequestBody();
-        log.debug("ChatGPT request body: {}", requestBody);
-
-        AIChatResponseContent AIChatResponseContent = getResponseContentStateful(threadId, chatGptRun);
-        chatGptRun.cancelRun();
-
-        return AIChatResponseContent;
+    if (isJsonString(responseText)) {
+      return extractResponseContent(responseText);
     }
+    return new AIChatResponseContent(responseText);
+  }
 
-    private AIChatResponseContent getResponseContentStateful(String threadId, ChatGptRun chatGptRun) {
-        return switch (chatGptRun.getFirstStepDetails().getType()) {
-            case TYPE_MESSAGE_CREATION -> retrieveThreadMessage(threadId, chatGptRun);
-            case TYPE_TOOL_CALLS -> getResponseContent(chatGptRun.getFirstStepToolCalls());
-            default -> throw new IllegalStateException("Unexpected Step Type in stateful ChatGpt response: " +
-                    chatGptRun);
-        };
-    }
-
-    private AIChatResponseContent retrieveThreadMessage(String threadId, ChatGptRun chatGptRun) {
-        ChatGptThreadMessage chatGptThreadMessage = new ChatGptThreadMessage(threadId, config);
-        ChatGptThreadMessageResponse threadMessageResponse = chatGptThreadMessage.retrieveMessage(
-                chatGptRun.getFirstStepDetails().getMessageCreation().getMessageId()
-        );
-        String responseText = threadMessageResponse.getContent().get(0).getText().getValue();
-        if (responseText == null) {
-            throw new RuntimeException("ChatGPT thread message response content is null");
-        }
-        if (isJsonString(responseText)) {
-            return extractResponseContent(responseText);
-        }
-        return new AIChatResponseContent(responseText);
-    }
-
-    private AIChatResponseContent extractResponseContent(String responseText) {
-        return getGson().fromJson(unwrapJsonCode(responseText), AIChatResponseContent.class);
-    }
+  private AIChatResponseContent extractResponseContent(String responseText) {
+    return getGson().fromJson(unwrapJsonCode(responseText), AIChatResponseContent.class);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptAssistant.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptAssistant.java
index 7f99dff..4954814 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptAssistant.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptAssistant.java
@@ -1,163 +1,172 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
-import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
-import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
-import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
-import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatParameters;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatTools;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatTool;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
-import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.git.GitRepoFiles;
-import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.prompt.AIChatGptPromptStatefulBase;
-import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.*;
-import com.googlesource.gerrit.plugins.aicodereview.utils.HashUtils;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.Request;
-
-import java.net.URI;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-
 import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatPromptFactory.getAIChatPromptStateful;
 import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt.ChatGptVectorStore.KEY_VECTOR_STORE_ID;
 import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.createTempFileWithContent;
 import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.sanitizeFilename;
 import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
 
+import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
+import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
+import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
+import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatParameters;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatTools;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatTool;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.git.GitRepoFiles;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.prompt.AIChatGptPromptStatefulBase;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptCreateAssistantRequestBody;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptFilesResponse;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptResponse;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptToolResources;
+import com.googlesource.gerrit.plugins.aicodereview.utils.HashUtils;
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.Request;
+
 @Slf4j
 public class ChatGptAssistant extends ClientBase {
-    private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
-    private final ChangeSetData changeSetData;
-    private final GerritChange change;
-    private final GitRepoFiles gitRepoFiles;
-    private final PluginDataHandler projectDataHandler;
-    private final PluginDataHandler assistantsDataHandler;
+  private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
+  private final ChangeSetData changeSetData;
+  private final GerritChange change;
+  private final GitRepoFiles gitRepoFiles;
+  private final PluginDataHandler projectDataHandler;
+  private final PluginDataHandler assistantsDataHandler;
 
-    private String description;
-    private String instructions;
-    private String model;
-    private Double temperature;
+  private String description;
+  private String instructions;
+  private String model;
+  private Double temperature;
 
-    public ChatGptAssistant(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            GitRepoFiles gitRepoFiles,
-            PluginDataHandlerProvider pluginDataHandlerProvider
-    ) {
-        super(config);
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.gitRepoFiles = gitRepoFiles;
-        this.projectDataHandler = pluginDataHandlerProvider.getProjectScope();
-        this.assistantsDataHandler = pluginDataHandlerProvider.getAssistantsWorkspace();
+  public ChatGptAssistant(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      GitRepoFiles gitRepoFiles,
+      PluginDataHandlerProvider pluginDataHandlerProvider) {
+    super(config);
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.gitRepoFiles = gitRepoFiles;
+    this.projectDataHandler = pluginDataHandlerProvider.getProjectScope();
+    this.assistantsDataHandler = pluginDataHandlerProvider.getAssistantsWorkspace();
+  }
+
+  public String setupAssistant() {
+    setupAssistantParameters();
+    String assistantIdHashKey = calculateAssistantIdHashKey();
+    log.info("Calculated assistant id hash key: {}", assistantIdHashKey);
+    String assistantId = assistantsDataHandler.getValue(assistantIdHashKey);
+    if (assistantId == null || config.getForceCreateAssistant()) {
+      log.debug("Setup Assistant for project {}", change.getProjectNameKey());
+      String vectorStoreId = createVectorStore();
+      assistantId = createAssistant(vectorStoreId);
+      assistantsDataHandler.setValue(assistantIdHashKey, assistantId);
+      log.info("Project assistant created with ID: {}", assistantId);
+    } else {
+      log.info("Project assistant found for the project. Assistant ID: {}", assistantId);
     }
+    return assistantId;
+  }
 
-    public String setupAssistant() {
-        setupAssistantParameters();
-        String assistantIdHashKey = calculateAssistantIdHashKey();
-        log.info("Calculated assistant id hash key: {}", assistantIdHashKey);
-        String assistantId = assistantsDataHandler.getValue(assistantIdHashKey);
-        if (assistantId == null || config.getForceCreateAssistant()) {
-            log.debug("Setup Assistant for project {}", change.getProjectNameKey());
-            String vectorStoreId = createVectorStore();
-            assistantId = createAssistant(vectorStoreId);
-            assistantsDataHandler.setValue(assistantIdHashKey, assistantId);
-            log.info("Project assistant created with ID: {}", assistantId);
-        }
-        else {
-            log.info("Project assistant found for the project. Assistant ID: {}", assistantId);
-        }
-        return assistantId;
+  public String createVectorStore() {
+    String vectorStoreId = projectDataHandler.getValue(KEY_VECTOR_STORE_ID);
+    if (vectorStoreId == null) {
+      String fileId = uploadRepoFiles();
+      ChatGptVectorStore vectorStore = new ChatGptVectorStore(fileId, config, change);
+      ChatGptResponse createVectorStoreResponse = vectorStore.createVectorStore();
+      vectorStoreId = createVectorStoreResponse.getId();
+      projectDataHandler.setValue(KEY_VECTOR_STORE_ID, vectorStoreId);
+      log.info("Vector Store created with ID: {}", vectorStoreId);
+    } else {
+      log.info("Vector Store found for the project. Vector Store ID: {}", vectorStoreId);
     }
+    return vectorStoreId;
+  }
 
-    public String createVectorStore() {
-        String vectorStoreId = projectDataHandler.getValue(KEY_VECTOR_STORE_ID);
-        if (vectorStoreId == null) {
-            String fileId = uploadRepoFiles();
-            ChatGptVectorStore vectorStore = new ChatGptVectorStore(fileId, config, change);
-            ChatGptResponse createVectorStoreResponse = vectorStore.createVectorStore();
-            vectorStoreId = createVectorStoreResponse.getId();
-            projectDataHandler.setValue(KEY_VECTOR_STORE_ID, vectorStoreId);
-            log.info("Vector Store created with ID: {}", vectorStoreId);
-        }
-        else {
-            log.info("Vector Store found for the project. Vector Store ID: {}", vectorStoreId);
-        }
-        return vectorStoreId;
-    }
+  public void flushAssistantIds() {
+    projectDataHandler.removeValue(KEY_VECTOR_STORE_ID);
+    assistantsDataHandler.destroy();
+  }
 
-    public void flushAssistantIds() {
-        projectDataHandler.removeValue(KEY_VECTOR_STORE_ID);
-        assistantsDataHandler.destroy();
-    }
+  private String uploadRepoFiles() {
+    String repoFiles = gitRepoFiles.getGitRepoFiles(config, change);
+    Path repoPath =
+        createTempFileWithContent(sanitizeFilename(change.getProjectName()), ".json", repoFiles);
+    ChatGptFiles chatGptFiles = new ChatGptFiles(config);
+    ChatGptFilesResponse chatGptFilesResponse = chatGptFiles.uploadFiles(repoPath);
 
-    private String uploadRepoFiles() {
-        String repoFiles = gitRepoFiles.getGitRepoFiles(config, change);
-        Path repoPath = createTempFileWithContent(sanitizeFilename(change.getProjectName()), ".json", repoFiles);
-        ChatGptFiles chatGptFiles = new ChatGptFiles(config);
-        ChatGptFilesResponse chatGptFilesResponse = chatGptFiles.uploadFiles(repoPath);
+    return chatGptFilesResponse.getId();
+  }
 
-        return chatGptFilesResponse.getId();
-    }
+  private String createAssistant(String vectorStoreId) {
+    Request request = createRequest(vectorStoreId);
+    log.debug("ChatGPT Create Assistant request: {}", request);
 
-    private String createAssistant(String vectorStoreId) {
-        Request request = createRequest(vectorStoreId);
-        log.debug("ChatGPT Create Assistant request: {}", request);
+    ChatGptResponse assistantResponse =
+        getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
+    log.debug("Assistant created: {}", assistantResponse);
 
-        ChatGptResponse assistantResponse = getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
-        log.debug("Assistant created: {}", assistantResponse);
+    return assistantResponse.getId();
+  }
 
-        return assistantResponse.getId();
-    }
+  private Request createRequest(String vectorStoreId) {
+    URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.assistantCreateUri());
+    log.debug("ChatGPT Create Assistant request URI: {}", uri);
+    AIChatTool[] tools =
+        new AIChatTool[] {new AIChatTool("file_search"), AIChatTools.retrieveFormatRepliesTool()};
+    ChatGptToolResources toolResources =
+        new ChatGptToolResources(
+            new ChatGptToolResources.VectorStoreIds(new String[] {vectorStoreId}));
+    ChatGptCreateAssistantRequestBody requestBody =
+        ChatGptCreateAssistantRequestBody.builder()
+            .name(AIChatGptPromptStatefulBase.DEFAULT_AI_CHAT_ASSISTANT_NAME)
+            .description(description)
+            .instructions(instructions)
+            .model(model)
+            .temperature(temperature)
+            .tools(tools)
+            .toolResources(toolResources)
+            .build();
+    log.debug("ChatGPT Create Assistant request body: {}", requestBody);
 
-    private Request createRequest(String vectorStoreId) {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.assistantCreateUri());
-        log.debug("ChatGPT Create Assistant request URI: {}", uri);
-        AIChatTool[] tools = new AIChatTool[] {
-                new AIChatTool("file_search"),
-                AIChatTools.retrieveFormatRepliesTool()
-        };
-        ChatGptToolResources toolResources = new ChatGptToolResources(
-                new ChatGptToolResources.VectorStoreIds(
-                        new String[] {vectorStoreId}
-                )
-        );
-        ChatGptCreateAssistantRequestBody requestBody = ChatGptCreateAssistantRequestBody.builder()
-                .name(AIChatGptPromptStatefulBase.DEFAULT_AI_CHAT_ASSISTANT_NAME)
-                .description(description)
-                .instructions(instructions)
-                .model(model)
-                .temperature(temperature)
-                .tools(tools)
-                .toolResources(toolResources)
-                .build();
-        log.debug("ChatGPT Create Assistant request body: {}", requestBody);
+    return httpClient.createRequestFromJson(uri.toString(), config, requestBody);
+  }
 
-        return httpClient.createRequestFromJson(uri.toString(), config, requestBody);
-    }
+  private void setupAssistantParameters() {
+    ChatGptPromptStateful chatGptPromptStateful =
+        getAIChatPromptStateful(config, changeSetData, change);
+    AIChatParameters AIChatParameters = new AIChatParameters(config, change.getIsCommentEvent());
 
-    private void setupAssistantParameters() {
-        ChatGptPromptStateful chatGptPromptStateful = getAIChatPromptStateful(config, changeSetData, change);
-        AIChatParameters AIChatParameters = new AIChatParameters(config, change.getIsCommentEvent());
+    description = chatGptPromptStateful.getDefaultGptAssistantDescription();
+    instructions = chatGptPromptStateful.getDefaultGptAssistantInstructions();
+    model = config.getAIModel();
+    temperature = AIChatParameters.getGptTemperature();
+  }
 
-        description = chatGptPromptStateful.getDefaultGptAssistantDescription();
-        instructions = chatGptPromptStateful.getDefaultGptAssistantInstructions();
-        model = config.getAIModel();
-        temperature = AIChatParameters.getGptTemperature();
-    }
-
-    private String calculateAssistantIdHashKey() {
-        return HashUtils.hashData(new ArrayList<>(List.of(
-                description,
-                instructions,
-                model,
-                temperature.toString()
-        )));
-    }
+  private String calculateAssistantIdHashKey() {
+    return HashUtils.hashData(
+        new ArrayList<>(List.of(description, instructions, model, temperature.toString())));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptFiles.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptFiles.java
index 22d1787..a377f98 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptFiles.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptFiles.java
@@ -1,49 +1,64 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.http.HttpClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptFilesResponse;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.*;
-import org.apache.http.NameValuePair;
-
 import java.io.File;
 import java.net.URI;
 import java.nio.file.Path;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
 
 @Slf4j
 public class ChatGptFiles extends ClientBase {
-    private final HttpClient httpClient = new HttpClient();
+  private final HttpClient httpClient = new HttpClient();
 
-    public ChatGptFiles(Configuration config) {
-        super(config);
-    }
+  public ChatGptFiles(Configuration config) {
+    super(config);
+  }
 
-    public ChatGptFilesResponse uploadFiles(Path repoPath) {
-        Request request = createUploadFileRequest(repoPath);
-        log.debug("ChatGPT Upload Files request: {}", request);
+  public ChatGptFilesResponse uploadFiles(Path repoPath) {
+    Request request = createUploadFileRequest(repoPath);
+    log.debug("ChatGPT Upload Files request: {}", request);
 
-        String response = httpClient.execute(request);
-        log.debug("ChatGPT Upload Files response: {}", response);
+    String response = httpClient.execute(request);
+    log.debug("ChatGPT Upload Files response: {}", response);
 
-        return getGson().fromJson(response, ChatGptFilesResponse.class);
-    }
+    return getGson().fromJson(response, ChatGptFilesResponse.class);
+  }
 
-    private Request createUploadFileRequest(Path repoPath) {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.filesCreateUri());
-        log.debug("ChatGPT Upload Files request URI: {}", uri);
-        File file = repoPath.toFile();
-        RequestBody requestBody = new MultipartBody.Builder()
-                .setType(MultipartBody.FORM)
-                .addFormDataPart("purpose", "assistants")
-                .addFormDataPart("file", file.getName(),
-                        RequestBody.create(file, MediaType.parse("application/json")))
-                .build();
+  private Request createUploadFileRequest(Path repoPath) {
+    URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.filesCreateUri());
+    log.debug("ChatGPT Upload Files request URI: {}", uri);
+    File file = repoPath.toFile();
+    RequestBody requestBody =
+        new MultipartBody.Builder()
+            .setType(MultipartBody.FORM)
+            .addFormDataPart("purpose", "assistants")
+            .addFormDataPart(
+                "file",
+                file.getName(),
+                RequestBody.create(file, MediaType.parse("application/json")))
+            .build();
 
-        return httpClient.createRequest(uri.toString(), config, requestBody, null);
-    }
+    return httpClient.createRequest(uri.toString(), config, requestBody, null);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptHttpClient.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptHttpClient.java
index 2ccc552..f4d3c19 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptHttpClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptHttpClient.java
@@ -1,15 +1,30 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.http.HttpClient;
+import java.util.Map;
 import okhttp3.Request;
 
-import java.util.Map;
-
 public class ChatGptHttpClient extends HttpClient {
-    private static final Map<String, String> BETA_VERSION_HEADER = Map.of("OpenAI-Beta", "assistants=v2");
+  private static final Map<String, String> BETA_VERSION_HEADER =
+      Map.of("OpenAI-Beta", "assistants=v2");
 
-    public Request createRequestFromJson(String uri, Configuration configuration, Object requestObject) {
-        return createRequestFromJson(uri, configuration, requestObject, BETA_VERSION_HEADER);
-    }
+  public Request createRequestFromJson(
+      String uri, Configuration configuration, Object requestObject) {
+    return createRequestFromJson(uri, configuration, requestObject, BETA_VERSION_HEADER);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptRun.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptRun.java
index 3325105..c93d0bc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptRun.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptRun.java
@@ -1,5 +1,22 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.ThreadUtils.threadSleep;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
@@ -9,172 +26,170 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.git.GitRepoFiles;
-import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.*;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptCreateRunRequest;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptListResponse;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptResponse;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptRunStepsResponse;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.Request;
 
-import java.net.URI;
-import java.util.*;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.ThreadUtils.threadSleep;
-
 @Slf4j
 public class ChatGptRun extends ClientBase {
-    private static final int RUN_POLLING_INTERVAL = 1000;
-    private static final int STEP_RETRIEVAL_INTERVAL = 10000;
-    private static final int MAX_STEP_RETRIEVAL_RETRIES = 3;
-    private static final Set<String> UNCOMPLETED_STATUSES = new HashSet<>(Arrays.asList(
-            "queued",
-            "in_progress",
-            "cancelling"
-    ));
-    public static final String COMPLETED_STATUS = "completed";
-    public static final String CANCELLED_STATUS = "cancelled";
+  private static final int RUN_POLLING_INTERVAL = 1000;
+  private static final int STEP_RETRIEVAL_INTERVAL = 10000;
+  private static final int MAX_STEP_RETRIEVAL_RETRIES = 3;
+  private static final Set<String> UNCOMPLETED_STATUSES =
+      new HashSet<>(Arrays.asList("queued", "in_progress", "cancelling"));
+  public static final String COMPLETED_STATUS = "completed";
+  public static final String CANCELLED_STATUS = "cancelled";
 
-    private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
-    private final ChangeSetData changeSetData;
-    private final GerritChange change;
-    private final String threadId;
-    private final GitRepoFiles gitRepoFiles;
-    private final PluginDataHandlerProvider pluginDataHandlerProvider;
+  private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
+  private final ChangeSetData changeSetData;
+  private final GerritChange change;
+  private final String threadId;
+  private final GitRepoFiles gitRepoFiles;
+  private final PluginDataHandlerProvider pluginDataHandlerProvider;
 
-    private ChatGptResponse runResponse;
-    private ChatGptListResponse stepResponse;
-    private String assistantId;
+  private ChatGptResponse runResponse;
+  private ChatGptListResponse stepResponse;
+  private String assistantId;
 
-    public ChatGptRun(
-            String threadId,
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            GitRepoFiles gitRepoFiles,
-            PluginDataHandlerProvider pluginDataHandlerProvider
-    ) {
-        super(config);
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.threadId = threadId;
-        this.gitRepoFiles = gitRepoFiles;
-        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  public ChatGptRun(
+      String threadId,
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      GitRepoFiles gitRepoFiles,
+      PluginDataHandlerProvider pluginDataHandlerProvider) {
+    super(config);
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.threadId = threadId;
+    this.gitRepoFiles = gitRepoFiles;
+    this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+  }
+
+  public void createRun() {
+    ChatGptAssistant chatGptAssistant =
+        new ChatGptAssistant(
+            config, changeSetData, change, gitRepoFiles, pluginDataHandlerProvider);
+    assistantId = chatGptAssistant.setupAssistant();
+
+    Request request = runCreateRequest();
+    log.info("ChatGPT Create Run request: {}", request);
+
+    runResponse = getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
+    log.info("Run created: {}", runResponse);
+  }
+
+  public void pollRunStep() {
+    for (int retries = 0; retries < MAX_STEP_RETRIEVAL_RETRIES; retries++) {
+      int pollingCount = pollRun();
+
+      Request stepsRequest = getStepsRequest();
+      log.debug("ChatGPT Retrieve Run Steps request: {}", stepsRequest);
+
+      String response = httpClient.execute(stepsRequest);
+      stepResponse = getGson().fromJson(response, ChatGptListResponse.class);
+      log.info("Run executed after {} polling requests: {}", pollingCount, stepResponse);
+      if (stepResponse.getData().isEmpty()) {
+        log.warn("Empty response from ChatGPT");
+        threadSleep(STEP_RETRIEVAL_INTERVAL);
+        continue;
+      }
+      return;
     }
+  }
 
-    public void createRun() {
-        ChatGptAssistant chatGptAssistant = new ChatGptAssistant(
-                config,
-                changeSetData,
-                change,
-                gitRepoFiles,
-                pluginDataHandlerProvider
-        );
-        assistantId = chatGptAssistant.setupAssistant();
+  public AIChatResponseMessage getFirstStepDetails() {
+    return getFirstStep().getStepDetails();
+  }
 
-        Request request = runCreateRequest();
-        log.info("ChatGPT Create Run request: {}", request);
+  public List<AIChatToolCall> getFirstStepToolCalls() {
+    return getFirstStepDetails().getToolCalls();
+  }
 
-        runResponse = getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
-        log.info("Run created: {}", runResponse);
+  public void cancelRun() {
+    if (getFirstStep().getStatus().equals(COMPLETED_STATUS)) return;
+
+    Request cancelRequest = getCancelRequest();
+    log.debug("ChatGPT Cancel Run request: {}", cancelRequest);
+    try {
+      String fullResponse = httpClient.execute(cancelRequest);
+      log.debug("ChatGPT Cancel Run Full response: {}", fullResponse);
+      ChatGptResponse response = getGson().fromJson(fullResponse, ChatGptResponse.class);
+      if (!response.getStatus().equals(CANCELLED_STATUS)) {
+        log.error("Unable to cancel run. Run cancel response: {}", fullResponse);
+      }
+    } catch (Exception e) {
+      log.error("Error cancelling run", e);
     }
+  }
 
-    public void pollRunStep() {
-        for (int retries = 0; retries < MAX_STEP_RETRIEVAL_RETRIES; retries++) {
-            int pollingCount = pollRun();
+  private int pollRun() {
+    int pollingCount = 0;
 
-            Request stepsRequest = getStepsRequest();
-            log.debug("ChatGPT Retrieve Run Steps request: {}", stepsRequest);
-
-            String response = httpClient.execute(stepsRequest);
-            stepResponse = getGson().fromJson(response, ChatGptListResponse.class);
-            log.info("Run executed after {} polling requests: {}", pollingCount, stepResponse);
-            if (stepResponse.getData().isEmpty()) {
-                log.warn("Empty response from ChatGPT");
-                threadSleep(STEP_RETRIEVAL_INTERVAL);
-                continue;
-            }
-            return;
-        }
+    while (UNCOMPLETED_STATUSES.contains(runResponse.getStatus())) {
+      pollingCount++;
+      log.debug("Polling request #{}", pollingCount);
+      threadSleep(RUN_POLLING_INTERVAL);
+      Request pollRequest = getPollRequest();
+      log.debug("ChatGPT Poll Run request: {}", pollRequest);
+      runResponse = getGson().fromJson(httpClient.execute(pollRequest), ChatGptResponse.class);
+      log.debug("ChatGPT Run response: {}", runResponse);
     }
+    return pollingCount;
+  }
 
-    public AIChatResponseMessage getFirstStepDetails() {
-        return getFirstStep().getStepDetails();
-    }
+  private ChatGptRunStepsResponse getFirstStep() {
+    return stepResponse.getData().get(0);
+  }
 
-    public List<AIChatToolCall> getFirstStepToolCalls() {
-        return getFirstStepDetails().getToolCalls();
-    }
+  private Request runCreateRequest() {
+    URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.runsUri(threadId));
+    log.debug("ChatGPT Create Run request URI: {}", uri);
+    ChatGptCreateRunRequest requestBody =
+        ChatGptCreateRunRequest.builder().assistantId(assistantId).build();
 
-    public void cancelRun() {
-        if (getFirstStep().getStatus().equals(COMPLETED_STATUS)) return;
+    return httpClient.createRequestFromJson(uri.toString(), config, requestBody);
+  }
 
-        Request cancelRequest = getCancelRequest();
-        log.debug("ChatGPT Cancel Run request: {}", cancelRequest);
-        try {
-            String fullResponse = httpClient.execute(cancelRequest);
-            log.debug("ChatGPT Cancel Run Full response: {}", fullResponse);
-            ChatGptResponse response = getGson().fromJson(fullResponse, ChatGptResponse.class);
-            if (!response.getStatus().equals(CANCELLED_STATUS)) {
-                log.error("Unable to cancel run. Run cancel response: {}", fullResponse);
-            }
-        }
-        catch (Exception e) {
-            log.error("Error cancelling run", e);
-        }
-    }
-
-    private int pollRun() {
-        int pollingCount = 0;
-
-        while (UNCOMPLETED_STATUSES.contains(runResponse.getStatus())) {
-            pollingCount++;
-            log.debug("Polling request #{}", pollingCount);
-            threadSleep(RUN_POLLING_INTERVAL);
-            Request pollRequest = getPollRequest();
-            log.debug("ChatGPT Poll Run request: {}", pollRequest);
-            runResponse = getGson().fromJson(httpClient.execute(pollRequest), ChatGptResponse.class);
-            log.debug("ChatGPT Run response: {}", runResponse);
-        }
-        return pollingCount;
-    }
-
-    private ChatGptRunStepsResponse getFirstStep() {
-        return stepResponse.getData().get(0);
-    }
-
-    private Request runCreateRequest() {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.runsUri(threadId));
-        log.debug("ChatGPT Create Run request URI: {}", uri);
-        ChatGptCreateRunRequest requestBody = ChatGptCreateRunRequest.builder()
-                .assistantId(assistantId)
-                .build();
-
-        return httpClient.createRequestFromJson(uri.toString(), config, requestBody);
-    }
-
-    private Request getPollRequest() {
-        URI uri = URI.create(config.getAIDomain()
+  private Request getPollRequest() {
+    URI uri =
+        URI.create(
+            config.getAIDomain()
                 + UriResourceLocatorStateful.runRetrieveUri(threadId, runResponse.getId()));
-        log.debug("ChatGPT Poll Run request URI: {}", uri);
+    log.debug("ChatGPT Poll Run request URI: {}", uri);
 
-        return getRunPollRequest(uri);
-    }
+    return getRunPollRequest(uri);
+  }
 
-    private Request getStepsRequest() {
-        URI uri = URI.create(config.getAIDomain()
+  private Request getStepsRequest() {
+    URI uri =
+        URI.create(
+            config.getAIDomain()
                 + UriResourceLocatorStateful.runStepsUri(threadId, runResponse.getId()));
-        log.debug("ChatGPT Run Steps request URI: {}", uri);
+    log.debug("ChatGPT Run Steps request URI: {}", uri);
 
-        return getRunPollRequest(uri);
-    }
+    return getRunPollRequest(uri);
+  }
 
-    private Request getCancelRequest() {
-        URI uri = URI.create(config.getAIDomain()
+  private Request getCancelRequest() {
+    URI uri =
+        URI.create(
+            config.getAIDomain()
                 + UriResourceLocatorStateful.runCancelUri(threadId, runResponse.getId()));
-        log.debug("ChatGPT Run Cancel request URI: {}", uri);
+    log.debug("ChatGPT Run Cancel request URI: {}", uri);
 
-        return httpClient.createRequestFromJson(uri.toString(), config, new Object());
-    }
+    return httpClient.createRequestFromJson(uri.toString(), config, new Object());
+  }
 
-    private Request getRunPollRequest(URI uri) {
-        return httpClient.createRequestFromJson(uri.toString(), config, null);
-    }
+  private Request getRunPollRequest(URI uri) {
+    return httpClient.createRequestFromJson(uri.toString(), config, null);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThread.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThread.java
index c8740ca..0c310e3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThread.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThread.java
@@ -1,54 +1,64 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptResponse;
+import java.net.URI;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.Request;
 
-import java.net.URI;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-
 @Slf4j
 public class ChatGptThread {
-    public static final String KEY_THREAD_ID = "threadId";
+  public static final String KEY_THREAD_ID = "threadId";
 
-    private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
-    private final Configuration config;
-    private final PluginDataHandler changeDataHandler;
+  private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
+  private final Configuration config;
+  private final PluginDataHandler changeDataHandler;
 
-    public ChatGptThread(
-            Configuration config,
-            PluginDataHandlerProvider pluginDataHandlerProvider
-    ) {
-        this.config = config;
-        this.changeDataHandler = pluginDataHandlerProvider.getChangeScope();
+  public ChatGptThread(Configuration config, PluginDataHandlerProvider pluginDataHandlerProvider) {
+    this.config = config;
+    this.changeDataHandler = pluginDataHandlerProvider.getChangeScope();
+  }
+
+  public String createThread() {
+    String threadId = changeDataHandler.getValue(KEY_THREAD_ID);
+    if (threadId == null) {
+      Request request = createThreadRequest();
+      log.debug("ChatGPT Create Thread request: {}", request);
+
+      ChatGptResponse threadResponse =
+          getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
+      log.info("Thread created: {}", threadResponse);
+      threadId = threadResponse.getId();
+      changeDataHandler.setValue(KEY_THREAD_ID, threadId);
+    } else {
+      log.info("Thread found for the Change Set. Thread ID: {}", threadId);
     }
+    return threadId;
+  }
 
-    public String createThread() {
-        String threadId = changeDataHandler.getValue(KEY_THREAD_ID);
-        if (threadId == null) {
-            Request request = createThreadRequest();
-            log.debug("ChatGPT Create Thread request: {}", request);
+  private Request createThreadRequest() {
+    URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.threadsUri());
+    log.debug("ChatGPT Create Thread request URI: {}", uri);
 
-            ChatGptResponse threadResponse = getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
-            log.info("Thread created: {}", threadResponse);
-            threadId = threadResponse.getId();
-            changeDataHandler.setValue(KEY_THREAD_ID, threadId);
-        }
-        else {
-            log.info("Thread found for the Change Set. Thread ID: {}", threadId);
-        }
-        return threadId;
-    }
-
-    private Request createThreadRequest() {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.threadsUri());
-        log.debug("ChatGPT Create Thread request URI: {}", uri);
-
-        return httpClient.createRequestFromJson(uri.toString(), config, new Object());
-    }
+    return httpClient.createRequestFromJson(uri.toString(), config, new Object());
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThreadMessage.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThreadMessage.java
index fbfd3a6..1be88af 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThreadMessage.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptThreadMessage.java
@@ -1,5 +1,22 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatPromptFactory.getAIChatPromptStateful;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
@@ -9,83 +26,83 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptResponse;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptThreadMessageResponse;
+import java.net.URI;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.Request;
 
-import java.net.URI;
-
-import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatPromptFactory.getAIChatPromptStateful;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-
 @Slf4j
 public class ChatGptThreadMessage extends ClientBase {
-    private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
-    private final String threadId;
+  private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
+  private final String threadId;
 
-    private ChangeSetData changeSetData;
-    private GerritChange change;
-    private String patchSet;
-    private AIChatRequestMessage addMessageRequestBody;
+  private ChangeSetData changeSetData;
+  private GerritChange change;
+  private String patchSet;
+  private AIChatRequestMessage addMessageRequestBody;
 
-    public ChatGptThreadMessage(String threadId, Configuration config) {
-        super(config);
-        this.threadId = threadId;
-    }
+  public ChatGptThreadMessage(String threadId, Configuration config) {
+    super(config);
+    this.threadId = threadId;
+  }
 
-    public ChatGptThreadMessage(
-            String threadId,
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritChange change,
-            String patchSet
-    ) {
-        this(threadId, config);
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.patchSet = patchSet;
-    }
+  public ChatGptThreadMessage(
+      String threadId,
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritChange change,
+      String patchSet) {
+    this(threadId, config);
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.patchSet = patchSet;
+  }
 
-    public ChatGptThreadMessageResponse retrieveMessage(String messageId) {
-        Request request = createRetrieveMessageRequest(messageId);
-        log.debug("ChatGPT Retrieve Thread Message request: {}", request);
-        ChatGptThreadMessageResponse threadMessageResponse = getGson().fromJson(
-                httpClient.execute(request), ChatGptThreadMessageResponse.class
-        );
-        log.info("Thread Message retrieved: {}", threadMessageResponse);
+  public ChatGptThreadMessageResponse retrieveMessage(String messageId) {
+    Request request = createRetrieveMessageRequest(messageId);
+    log.debug("ChatGPT Retrieve Thread Message request: {}", request);
+    ChatGptThreadMessageResponse threadMessageResponse =
+        getGson().fromJson(httpClient.execute(request), ChatGptThreadMessageResponse.class);
+    log.info("Thread Message retrieved: {}", threadMessageResponse);
 
-        return threadMessageResponse;
-    }
+    return threadMessageResponse;
+  }
 
-    public void addMessage() {
-        Request request = addMessageRequest();
-        log.debug("ChatGPT Add Message request: {}", request);
+  public void addMessage() {
+    Request request = addMessageRequest();
+    log.debug("ChatGPT Add Message request: {}", request);
 
-        ChatGptResponse addMessageResponse = getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
-        log.info("Message added: {}", addMessageResponse);
-    }
+    ChatGptResponse addMessageResponse =
+        getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
+    log.info("Message added: {}", addMessageResponse);
+  }
 
-    public String getAddMessageRequestBody() {
-        return getGson().toJson(addMessageRequestBody);
-    }
+  public String getAddMessageRequestBody() {
+    return getGson().toJson(addMessageRequestBody);
+  }
 
-    private Request createRetrieveMessageRequest(String messageId) {
-        URI uri = URI.create(config.getAIDomain() +
-                UriResourceLocatorStateful.threadMessageRetrieveUri(threadId, messageId));
-        log.debug("ChatGPT Retrieve Thread Message request URI: {}", uri);
+  private Request createRetrieveMessageRequest(String messageId) {
+    URI uri =
+        URI.create(
+            config.getAIDomain()
+                + UriResourceLocatorStateful.threadMessageRetrieveUri(threadId, messageId));
+    log.debug("ChatGPT Retrieve Thread Message request URI: {}", uri);
 
-        return httpClient.createRequestFromJson(uri.toString(), config, null);
-    }
+    return httpClient.createRequestFromJson(uri.toString(), config, null);
+  }
 
-    private Request addMessageRequest() {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.threadMessagesUri(threadId));
-        log.debug("ChatGPT Add Message request URI: {}", uri);
-        ChatGptPromptStateful chatGptPromptStateful = getAIChatPromptStateful(config, changeSetData, change);
-        addMessageRequestBody = AIChatRequestMessage.builder()
-                .role("user")
-                .content(chatGptPromptStateful.getDefaultGptThreadReviewMessage(patchSet))
-                .build();
-        log.debug("ChatGPT Add Message request body: {}", addMessageRequestBody);
+  private Request addMessageRequest() {
+    URI uri =
+        URI.create(config.getAIDomain() + UriResourceLocatorStateful.threadMessagesUri(threadId));
+    log.debug("ChatGPT Add Message request URI: {}", uri);
+    ChatGptPromptStateful chatGptPromptStateful =
+        getAIChatPromptStateful(config, changeSetData, change);
+    addMessageRequestBody =
+        AIChatRequestMessage.builder()
+            .role("user")
+            .content(chatGptPromptStateful.getDefaultGptThreadReviewMessage(patchSet))
+            .build();
+    log.debug("ChatGPT Add Message request body: {}", addMessageRequestBody);
 
-        return httpClient.createRequestFromJson(uri.toString(), config, addMessageRequestBody);
-    }
+    return httpClient.createRequestFromJson(uri.toString(), config, addMessageRequestBody);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptVectorStore.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptVectorStore.java
index a4be290..313132f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptVectorStore.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/chatgpt/ChatGptVectorStore.java
@@ -1,51 +1,67 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.ClientBase;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
-import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.*;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptCreateVectorStoreRequest;
+import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptResponse;
+import java.net.URI;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.Request;
 
-import java.net.URI;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-
 @Slf4j
 public class ChatGptVectorStore extends ClientBase {
-    public static final String KEY_VECTOR_STORE_ID = "vectorStoreId";
+  public static final String KEY_VECTOR_STORE_ID = "vectorStoreId";
 
-    private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
-    private final String fileId;
-    private final GerritChange change;
+  private final ChatGptHttpClient httpClient = new ChatGptHttpClient();
+  private final String fileId;
+  private final GerritChange change;
 
-    public ChatGptVectorStore(String fileId, Configuration config, GerritChange change) {
-        super(config);
-        this.fileId = fileId;
-        this.change = change;
-    }
+  public ChatGptVectorStore(String fileId, Configuration config, GerritChange change) {
+    super(config);
+    this.fileId = fileId;
+    this.change = change;
+  }
 
-    public ChatGptResponse createVectorStore() {
-        Request request = vectorStoreCreateRequest();
-        log.debug("ChatGPT Create Vector Store request: {}", request);
+  public ChatGptResponse createVectorStore() {
+    Request request = vectorStoreCreateRequest();
+    log.debug("ChatGPT Create Vector Store request: {}", request);
 
-        ChatGptResponse createVectorStoreResponse = getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
-        log.info("Vector Store created: {}", createVectorStoreResponse);
+    ChatGptResponse createVectorStoreResponse =
+        getGson().fromJson(httpClient.execute(request), ChatGptResponse.class);
+    log.info("Vector Store created: {}", createVectorStoreResponse);
 
-        return createVectorStoreResponse;
-    }
+    return createVectorStoreResponse;
+  }
 
-    private Request vectorStoreCreateRequest() {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.vectorStoreCreateUri());
-        log.debug("ChatGPT Create Vector Store request URI: {}", uri);
+  private Request vectorStoreCreateRequest() {
+    URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateful.vectorStoreCreateUri());
+    log.debug("ChatGPT Create Vector Store request URI: {}", uri);
 
-        ChatGptCreateVectorStoreRequest requestBody = ChatGptCreateVectorStoreRequest.builder()
-                .name(change.getProjectName())
-                .fileIds(new String[] { fileId })
-                .build();
+    ChatGptCreateVectorStoreRequest requestBody =
+        ChatGptCreateVectorStoreRequest.builder()
+            .name(change.getProjectName())
+            .fileIds(new String[] {fileId})
+            .build();
 
-        log.debug("ChatGPT Create Vector Store request body: {}", requestBody);
-        return httpClient.createRequestFromJson(uri.toString(), config, requestBody);
-    }
+    log.debug("ChatGPT Create Vector Store request body: {}", requestBody);
+    return httpClient.createRequestFromJson(uri.toString(), config, requestBody);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetHelper.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetHelper.java
index 87d0184..4f8f948 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetHelper.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetHelper.java
@@ -1,46 +1,64 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit;
 
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.COMMIT_MESSAGE_FILTER_OUT_PREFIXES;
 import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_COMMIT_MESSAGE_PREFIX;
 
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import lombok.extern.slf4j.Slf4j;
+
 @Slf4j
 public class GerritClientPatchSetHelper {
-    private static final Pattern EXTRACT_B_FILENAMES_FROM_PATCH_SET = Pattern.compile("^diff --git .*? b/(.*)$",
-            Pattern.MULTILINE);
+  private static final Pattern EXTRACT_B_FILENAMES_FROM_PATCH_SET =
+      Pattern.compile("^diff --git .*? b/(.*)$", Pattern.MULTILINE);
 
-    public static String filterPatchWithCommitMessage(String formattedPatch) {
-        // Remove Patch heading up to the Date annotation, so that the commit message is included. Additionally, remove
-        // the change type between brackets
-        Pattern CONFIG_ID_HEADING_PATTERN = Pattern.compile(
-                "^.*?" + GERRIT_COMMIT_MESSAGE_PREFIX + "(?:\\[[^\\]]+\\] )?",
-                Pattern.DOTALL
-        );
-        return CONFIG_ID_HEADING_PATTERN.matcher(formattedPatch).replaceAll(GERRIT_COMMIT_MESSAGE_PREFIX);
-    }
+  public static String filterPatchWithCommitMessage(String formattedPatch) {
+    // Remove Patch heading up to the Date annotation, so that the commit message is included.
+    // Additionally, remove
+    // the change type between brackets
+    Pattern CONFIG_ID_HEADING_PATTERN =
+        Pattern.compile(
+            "^.*?" + GERRIT_COMMIT_MESSAGE_PREFIX + "(?:\\[[^\\]]+\\] )?", Pattern.DOTALL);
+    return CONFIG_ID_HEADING_PATTERN
+        .matcher(formattedPatch)
+        .replaceAll(GERRIT_COMMIT_MESSAGE_PREFIX);
+  }
 
-    public static String filterPatchWithoutCommitMessage(GerritChange change, String formattedPatch) {
-        // Remove Patch heading up to the Change-Id annotation
-        Pattern CONFIG_ID_HEADING_PATTERN = Pattern.compile(
-                "^.*?" + COMMIT_MESSAGE_FILTER_OUT_PREFIXES.get("CHANGE_ID") + " " + change.getChangeKey().get(),
-                Pattern.DOTALL
-        );
-        return CONFIG_ID_HEADING_PATTERN.matcher(formattedPatch).replaceAll("");
-    }
+  public static String filterPatchWithoutCommitMessage(GerritChange change, String formattedPatch) {
+    // Remove Patch heading up to the Change-Id annotation
+    Pattern CONFIG_ID_HEADING_PATTERN =
+        Pattern.compile(
+            "^.*?"
+                + COMMIT_MESSAGE_FILTER_OUT_PREFIXES.get("CHANGE_ID")
+                + " "
+                + change.getChangeKey().get(),
+            Pattern.DOTALL);
+    return CONFIG_ID_HEADING_PATTERN.matcher(formattedPatch).replaceAll("");
+  }
 
-    public static List<String> extractFilesFromPatch(String formattedPatch) {
-        Matcher extractFilenameMatcher = EXTRACT_B_FILENAMES_FROM_PATCH_SET.matcher(formattedPatch);
-        List<String> files = new ArrayList<>();
-        while (extractFilenameMatcher.find()) {
-            files.add(extractFilenameMatcher.group(1));
-        }
-        return files;
+  public static List<String> extractFilesFromPatch(String formattedPatch) {
+    Matcher extractFilenameMatcher = EXTRACT_B_FILENAMES_FROM_PATCH_SET.matcher(formattedPatch);
+    List<String> files = new ArrayList<>();
+    while (extractFilenameMatcher.find()) {
+      files.add(extractFilenameMatcher.group(1));
     }
+    return files;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetStateful.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetStateful.java
index 13af663..9cd1d3a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetStateful.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/gerrit/GerritClientPatchSetStateful.java
@@ -1,64 +1,82 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit;
 
+import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit.GerritClientPatchSetHelper.extractFilesFromPatch;
+import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit.GerritClientPatchSetHelper.filterPatchWithCommitMessage;
+import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit.GerritClientPatchSetHelper.filterPatchWithoutCommitMessage;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.gerrit.GerritClientPatchSet;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
+import java.util.List;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.gerrit.GerritClientPatchSetHelper.*;
-
 @Slf4j
-public class GerritClientPatchSetStateful extends com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientPatchSet implements GerritClientPatchSet {
-    private GerritChange change;
+public class GerritClientPatchSetStateful
+    extends com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit
+        .GerritClientPatchSet
+    implements GerritClientPatchSet {
+  private GerritChange change;
 
-    @VisibleForTesting
-    @Inject
-    public GerritClientPatchSetStateful(Configuration config, AccountCache accountCache) {
-        super(config, accountCache);
+  @VisibleForTesting
+  @Inject
+  public GerritClientPatchSetStateful(Configuration config, AccountCache accountCache) {
+    super(config, accountCache);
+  }
+
+  public String getPatchSet(ChangeSetData changeSetData, GerritChange change) throws Exception {
+    if (change.getIsCommentEvent()) return "";
+    this.change = change;
+
+    String formattedPatch = getPatchFromGerrit();
+    List<String> files = extractFilesFromPatch(formattedPatch);
+    retrieveFileDiff(change, files, revisionBase);
+
+    return formattedPatch;
+  }
+
+  private String getPatchFromGerrit() throws Exception {
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      String formattedPatch =
+          config
+              .getGerritApi()
+              .changes()
+              .id(
+                  change.getProjectName(),
+                  change.getBranchNameKey().shortName(),
+                  change.getChangeKey().get())
+              .current()
+              .patch()
+              .asString();
+      log.debug("Formatted Patch retrieved: {}", formattedPatch);
+
+      return filterPatch(formattedPatch);
     }
+  }
 
-    public String getPatchSet(ChangeSetData changeSetData, GerritChange change) throws Exception {
-        if (change.getIsCommentEvent()) return "";
-        this.change = change;
-
-        String formattedPatch = getPatchFromGerrit();
-        List<String> files = extractFilesFromPatch(formattedPatch);
-        retrieveFileDiff(change, files, revisionBase);
-
-        return formattedPatch;
+  private String filterPatch(String formattedPatch) {
+    if (config.getAIReviewCommitMessages()) {
+      return filterPatchWithCommitMessage(formattedPatch);
+    } else {
+      return filterPatchWithoutCommitMessage(change, formattedPatch);
     }
-
-    private String getPatchFromGerrit() throws Exception {
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            String formattedPatch = config
-                .getGerritApi()
-                .changes()
-                .id(
-                        change.getProjectName(),
-                        change.getBranchNameKey().shortName(),
-                        change.getChangeKey().get())
-                .current()
-                .patch()
-                .asString();
-            log.debug("Formatted Patch retrieved: {}", formattedPatch);
-
-            return filterPatch(formattedPatch);
-        }
-    }
-
-    private String filterPatch(String formattedPatch) {
-        if (config.getAIReviewCommitMessages()) {
-            return filterPatchWithCommitMessage(formattedPatch);
-        }
-        else {
-            return filterPatchWithoutCommitMessage(change, formattedPatch);
-        }
-    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/git/GitRepoFiles.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/git/GitRepoFiles.java
index b618a43..e746f36 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/git/GitRepoFiles.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/api/git/GitRepoFiles.java
@@ -1,80 +1,100 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.git;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.matchesExtensionList;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.lib.*;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils.matchesExtensionList;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
 
 @Slf4j
 public class GitRepoFiles {
-    public static final String REPO_PATTERN = "git/%s.git";
+  public static final String REPO_PATTERN = "git/%s.git";
 
-    private List<String> enabledFileExtensions;
+  private List<String> enabledFileExtensions;
 
-    public String getGitRepoFiles(Configuration config, GerritChange change) {
-        enabledFileExtensions = config.getEnabledFileExtensions();
-        log.debug("Open Repo from {}", change.getProjectNameKey());
-        String repoPath = String.format(REPO_PATTERN, change.getProjectNameKey().toString());
-        try {
-            Repository repository = openRepository(repoPath);
-            log.debug("Open Repo path {}", repoPath);
-            Map<String, String> filesWithContent = listFilesWithContent(repository);
+  public String getGitRepoFiles(Configuration config, GerritChange change) {
+    enabledFileExtensions = config.getEnabledFileExtensions();
+    log.debug("Open Repo from {}", change.getProjectNameKey());
+    String repoPath = String.format(REPO_PATTERN, change.getProjectNameKey().toString());
+    try {
+      Repository repository = openRepository(repoPath);
+      log.debug("Open Repo path {}", repoPath);
+      Map<String, String> filesWithContent = listFilesWithContent(repository);
 
-            return getGson().toJson(filesWithContent);
-        } catch (IOException | GitAPIException e) {
-            throw new RuntimeException("Failed to retrieve files in master branch: ", e);
+      return getGson().toJson(filesWithContent);
+    } catch (IOException | GitAPIException e) {
+      throw new RuntimeException("Failed to retrieve files in master branch: ", e);
+    }
+  }
+
+  public Map<String, String> listFilesWithContent(Repository repository)
+      throws IOException, GitAPIException {
+    Map<String, String> filesWithContent = new HashMap<>();
+    try (ObjectReader reader = repository.newObjectReader();
+        RevWalk revWalk = new RevWalk(repository)) {
+      ObjectId lastCommitId = repository.resolve(Constants.R_HEADS + "master");
+      RevCommit commit = revWalk.parseCommit(lastCommitId);
+      RevTree tree = commit.getTree();
+
+      try (TreeWalk treeWalk = new TreeWalk(repository)) {
+        treeWalk.addTree(tree);
+        treeWalk.setRecursive(true);
+        treeWalk.setFilter(TreeFilter.ANY_DIFF);
+
+        while (treeWalk.next()) {
+          String path = treeWalk.getPathString();
+          if (!matchesExtensionList(path, enabledFileExtensions)) continue;
+          ObjectId objectId = treeWalk.getObjectId(0);
+          byte[] bytes = reader.open(objectId).getBytes();
+          String content =
+              new String(bytes, StandardCharsets.UTF_8); // Assumes text files with UTF-8 encoding
+          log.debug("Repo File loaded: {}", path);
+          filesWithContent.put(path, content);
         }
+      }
     }
+    return filesWithContent;
+  }
 
-    public Map<String, String> listFilesWithContent(Repository repository) throws IOException, GitAPIException {
-        Map<String, String> filesWithContent = new HashMap<>();
-        try (ObjectReader reader = repository.newObjectReader();
-             RevWalk revWalk = new RevWalk(repository)) {
-            ObjectId lastCommitId = repository.resolve(Constants.R_HEADS + "master");
-            RevCommit commit = revWalk.parseCommit(lastCommitId);
-            RevTree tree = commit.getTree();
-
-            try (TreeWalk treeWalk = new TreeWalk(repository)) {
-                treeWalk.addTree(tree);
-                treeWalk.setRecursive(true);
-                treeWalk.setFilter(TreeFilter.ANY_DIFF);
-
-                while (treeWalk.next()) {
-                    String path = treeWalk.getPathString();
-                    if (!matchesExtensionList(path, enabledFileExtensions)) continue;
-                    ObjectId objectId = treeWalk.getObjectId(0);
-                    byte[] bytes = reader.open(objectId).getBytes();
-                    String content = new String(bytes, StandardCharsets.UTF_8); // Assumes text files with UTF-8 encoding
-                    log.debug("Repo File loaded: {}", path);
-                    filesWithContent.put(path, content);
-                }
-            }
-        }
-        return filesWithContent;
-    }
-
-    public Repository openRepository(String path) throws IOException {
-        FileRepositoryBuilder builder = new FileRepositoryBuilder();
-        return builder.setGitDir(new File(path))
-                .readEnvironment()
-                .findGitDir()
-                .setMustExist(true)
-                .build();
-    }
+  public Repository openRepository(String path) throws IOException {
+    FileRepositoryBuilder builder = new FileRepositoryBuilder();
+    return builder
+        .setGitDir(new File(path))
+        .readEnvironment()
+        .findGitDir()
+        .setMustExist(true)
+        .build();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatDataPromptRequestsStateful.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatDataPromptRequestsStateful.java
index 25b4353..75be8d6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatDataPromptRequestsStateful.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatDataPromptRequestsStateful.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -7,13 +21,13 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.GerritClientData;
 
-public class AIChatDataPromptRequestsStateful extends AIChatDataPromptRequests implements ChatAIDataPrompt {
-    public AIChatDataPromptRequestsStateful(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        super(config, changeSetData, gerritClientData, localizer);
-    }
+public class AIChatDataPromptRequestsStateful extends AIChatDataPromptRequests
+    implements ChatAIDataPrompt {
+  public AIChatDataPromptRequestsStateful(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    super(config, changeSetData, gerritClientData, localizer);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulBase.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulBase.java
index d1df98b..13edb30 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulBase.java
@@ -1,65 +1,81 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.DOT;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithSpace;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatGptPrompt;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ArrayList;
 import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
-public abstract class AIChatGptPromptStatefulBase extends AIChatGptPrompt implements ChatGptPromptStateful {
-    public static String DEFAULT_AI_CHAT_ASSISTANT_NAME;
-    public static String DEFAULT_AI_CHAT_ASSISTANT_DESCRIPTION;
-    public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS;
-    public static String DEFAULT_AI_CHAT_MESSAGE_REVIEW;
+public abstract class AIChatGptPromptStatefulBase extends AIChatGptPrompt
+    implements ChatGptPromptStateful {
+  public static String DEFAULT_AI_CHAT_ASSISTANT_NAME;
+  public static String DEFAULT_AI_CHAT_ASSISTANT_DESCRIPTION;
+  public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS;
+  public static String DEFAULT_AI_CHAT_MESSAGE_REVIEW;
 
-    protected final ChangeSetData changeSetData;
+  protected final ChangeSetData changeSetData;
 
-    private final GerritChange change;
+  private final GerritChange change;
 
-    public AIChatGptPromptStatefulBase(Configuration config, ChangeSetData changeSetData, GerritChange change) {
-        super(config);
-        this.changeSetData = changeSetData;
-        this.change = change;
-        this.isCommentEvent = change.getIsCommentEvent();
-        // Avoid repeated loading of prompt constants
-        if (DEFAULT_AI_CHAT_ASSISTANT_NAME == null) {
-            loadDefaultPrompts("promptsStateful");
-        }
+  public AIChatGptPromptStatefulBase(
+      Configuration config, ChangeSetData changeSetData, GerritChange change) {
+    super(config);
+    this.changeSetData = changeSetData;
+    this.change = change;
+    this.isCommentEvent = change.getIsCommentEvent();
+    // Avoid repeated loading of prompt constants
+    if (DEFAULT_AI_CHAT_ASSISTANT_NAME == null) {
+      loadDefaultPrompts("promptsStateful");
     }
+  }
 
-    public String getDefaultGptAssistantDescription() {
-        return String.format(DEFAULT_AI_CHAT_ASSISTANT_DESCRIPTION, change.getProjectName());
-    }
+  public String getDefaultGptAssistantDescription() {
+    return String.format(DEFAULT_AI_CHAT_ASSISTANT_DESCRIPTION, change.getProjectName());
+  }
 
-    public abstract void addGptAssistantInstructions(List<String> instructions);
+  public abstract void addGptAssistantInstructions(List<String> instructions);
 
-    public abstract String getAIRequestDataPrompt();
+  public abstract String getAIRequestDataPrompt();
 
-    public String getDefaultGptAssistantInstructions() {
-        List<String> instructions = new ArrayList<>(List.of(
+  public String getDefaultGptAssistantInstructions() {
+    List<String> instructions =
+        new ArrayList<>(
+            List.of(
                 DEFAULT_AI_CHAT_SYSTEM_PROMPT + DOT,
-                String.format(DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS, change.getProjectName())
-        ));
-        addGptAssistantInstructions(instructions);
+                String.format(DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS, change.getProjectName())));
+    addGptAssistantInstructions(instructions);
 
-        return joinWithSpace(instructions);
-    }
+    return joinWithSpace(instructions);
+  }
 
-    public String getDefaultGptThreadReviewMessage(String patchSet) {
-        String gptRequestDataPrompt = getAIRequestDataPrompt();
-        if (gptRequestDataPrompt != null && !gptRequestDataPrompt.isEmpty()) {
-            log.debug("Request User Prompt retrieved: {}", gptRequestDataPrompt);
-            return gptRequestDataPrompt;
-        }
-        else {
-            return String.format(DEFAULT_AI_CHAT_MESSAGE_REVIEW, patchSet);
-        }
+  public String getDefaultGptThreadReviewMessage(String patchSet) {
+    String gptRequestDataPrompt = getAIRequestDataPrompt();
+    if (gptRequestDataPrompt != null && !gptRequestDataPrompt.isEmpty()) {
+      log.debug("Request User Prompt retrieved: {}", gptRequestDataPrompt);
+      return gptRequestDataPrompt;
+    } else {
+      return String.format(DEFAULT_AI_CHAT_MESSAGE_REVIEW, patchSet);
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulRequests.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulRequests.java
index 9ccbbbd..42aecb3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulRequests.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulRequests.java
@@ -1,35 +1,50 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
+import java.util.List;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.List;
-
 @Slf4j
-public class AIChatGptPromptStatefulRequests extends AIChatGptPromptStatefulBase implements ChatGptPromptStateful {
-    public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REQUESTS;
+public class AIChatGptPromptStatefulRequests extends AIChatGptPromptStatefulBase
+    implements ChatGptPromptStateful {
+  public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REQUESTS;
 
-    public AIChatGptPromptStatefulRequests(Configuration config, ChangeSetData changeSetData, GerritChange change) {
-        super(config, changeSetData, change);
-        if (DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REQUESTS == null) {
-            loadDefaultPrompts("promptsStatefulRequests");
-        }
+  public AIChatGptPromptStatefulRequests(
+      Configuration config, ChangeSetData changeSetData, GerritChange change) {
+    super(config, changeSetData, change);
+    if (DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REQUESTS == null) {
+      loadDefaultPrompts("promptsStatefulRequests");
     }
+  }
 
-    @Override
-    public void addGptAssistantInstructions(List<String> instructions) {
-       instructions.addAll(List.of(
-                DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REQUESTS,
-               getCommentRequestPrompt(changeSetData.getCommentPropertiesSize())
-        ));
-    }
+  @Override
+  public void addGptAssistantInstructions(List<String> instructions) {
+    instructions.addAll(
+        List.of(
+            DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REQUESTS,
+            getCommentRequestPrompt(changeSetData.getCommentPropertiesSize())));
+  }
 
-    @Override
-    public String getAIRequestDataPrompt() {
-        if (changeSetData == null ) return null;
-        return changeSetData.getReviewAIDataPrompt();
-    }
+  @Override
+  public String getAIRequestDataPrompt() {
+    if (changeSetData == null) return null;
+    return changeSetData.getReviewAIDataPrompt();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulReview.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulReview.java
index 8f006d6..105aa37 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/client/prompt/AIChatGptPromptStatefulReview.java
@@ -1,55 +1,72 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.COLON_SPACE;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.getNumberedList;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ArrayList;
 import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
-public class AIChatGptPromptStatefulReview extends AIChatGptPromptStatefulBase implements ChatGptPromptStateful {
-    private static final String RULE_NUMBER_PREFIX = "RULE #";
+public class AIChatGptPromptStatefulReview extends AIChatGptPromptStatefulBase
+    implements ChatGptPromptStateful {
+  private static final String RULE_NUMBER_PREFIX = "RULE #";
 
-    public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REVIEW;
-    public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_DONT_GUESS_CODE;
-    public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_HISTORY;
+  public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REVIEW;
+  public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_DONT_GUESS_CODE;
+  public static String DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_HISTORY;
 
-    public AIChatGptPromptStatefulReview(Configuration config, ChangeSetData changeSetData, GerritChange change) {
-        super(config, changeSetData, change);
-        if (DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REVIEW == null) {
-            loadDefaultPrompts("promptsStatefulReview");
-        }
+  public AIChatGptPromptStatefulReview(
+      Configuration config, ChangeSetData changeSetData, GerritChange change) {
+    super(config, changeSetData, change);
+    if (DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REVIEW == null) {
+      loadDefaultPrompts("promptsStatefulReview");
     }
+  }
 
-    @Override
-    public void addGptAssistantInstructions(List<String> instructions) {
-        instructions.addAll(List.of(
-                getGptAssistantInstructionsReview(),
-                getPatchSetReviewPrompt()
-        ));
-        if (config.getAIReviewCommitMessages()) {
-            instructions.add(getReviewPromptCommitMessages());
-        }
+  @Override
+  public void addGptAssistantInstructions(List<String> instructions) {
+    instructions.addAll(List.of(getGptAssistantInstructionsReview(), getPatchSetReviewPrompt()));
+    if (config.getAIReviewCommitMessages()) {
+      instructions.add(getReviewPromptCommitMessages());
     }
+  }
 
-    @Override
-    public String getAIRequestDataPrompt() {
-        return null;
-    }
+  @Override
+  public String getAIRequestDataPrompt() {
+    return null;
+  }
 
-    private String getGptAssistantInstructionsReview() {
-        return String.format(DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REVIEW, joinWithNewLine(getNumberedList(
-                new ArrayList<>(List.of(
+  private String getGptAssistantInstructionsReview() {
+    return String.format(
+        DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_REVIEW,
+        joinWithNewLine(
+            getNumberedList(
+                new ArrayList<>(
+                    List.of(
                         DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT,
                         DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_DONT_GUESS_CODE,
-                        DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_HISTORY
-                )),
-                RULE_NUMBER_PREFIX, COLON_SPACE
-        )));
-    }
+                        DEFAULT_AI_CHAT_ASSISTANT_INSTRUCTIONS_HISTORY)),
+                RULE_NUMBER_PREFIX,
+                COLON_SPACE)));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateAssistantRequestBody.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateAssistantRequestBody.java
index 94c0df4..9d52912 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateAssistantRequestBody.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateAssistantRequestBody.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import com.google.gson.annotations.SerializedName;
@@ -8,12 +22,13 @@
 @Data
 @Builder
 public class ChatGptCreateAssistantRequestBody {
-    private String name;
-    private String description;
-    private String instructions;
-    private String model;
-    private Double temperature;
-    private AIChatTool[] tools;
-    @SerializedName("tool_resources")
-    private ChatGptToolResources toolResources;
+  private String name;
+  private String description;
+  private String instructions;
+  private String model;
+  private Double temperature;
+  private AIChatTool[] tools;
+
+  @SerializedName("tool_resources")
+  private ChatGptToolResources toolResources;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateRunRequest.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateRunRequest.java
index 0a0aae0..c5e769e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateRunRequest.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateRunRequest.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import com.google.gson.annotations.SerializedName;
@@ -7,6 +21,6 @@
 @Data
 @Builder
 public class ChatGptCreateRunRequest {
-    @SerializedName("assistant_id")
-    private String assistantId;
+  @SerializedName("assistant_id")
+  private String assistantId;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateVectorStoreRequest.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateVectorStoreRequest.java
index b755054..9051559 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateVectorStoreRequest.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptCreateVectorStoreRequest.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import com.google.gson.annotations.SerializedName;
@@ -7,7 +21,8 @@
 @Data
 @Builder
 public class ChatGptCreateVectorStoreRequest {
-    private String name;
-    @SerializedName("file_ids")
-    private String[] fileIds;
+  private String name;
+
+  @SerializedName("file_ids")
+  private String[] fileIds;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptFilesResponse.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptFilesResponse.java
index 88d2cb6..bc73af0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptFilesResponse.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptFilesResponse.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import lombok.Data;
@@ -6,5 +20,5 @@
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class ChatGptFilesResponse extends ChatGptResponse {
-    private String filename;
+  private String filename;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptListResponse.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptListResponse.java
index 14363a7..9b719ef 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptListResponse.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptListResponse.java
@@ -1,11 +1,24 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
-import lombok.Data;
-
 import java.util.List;
+import lombok.Data;
 
 @Data
 public class ChatGptListResponse {
-    private String object;
-    private List<ChatGptRunStepsResponse> data;
+  private String object;
+  private List<ChatGptRunStepsResponse> data;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptResponse.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptResponse.java
index 84d5ae5..72af0be 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptResponse.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptResponse.java
@@ -1,10 +1,24 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import lombok.Data;
 
 @Data
 public class ChatGptResponse {
-    private String id;
-    private String object;
-    private String status;
+  private String id;
+  private String object;
+  private String status;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptRunStepsResponse.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptRunStepsResponse.java
index b70847a..9a60325 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptRunStepsResponse.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptRunStepsResponse.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import com.google.gson.annotations.SerializedName;
@@ -8,6 +22,6 @@
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class ChatGptRunStepsResponse extends ChatGptResponse {
-    @SerializedName("step_details")
-    private AIChatResponseMessage stepDetails;
+  @SerializedName("step_details")
+  private AIChatResponseMessage stepDetails;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptThreadMessageResponse.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptThreadMessageResponse.java
index 6c61254..d96d35a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptThreadMessageResponse.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptThreadMessageResponse.java
@@ -1,23 +1,36 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
+import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
-import java.util.List;
-
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class ChatGptThreadMessageResponse extends ChatGptResponse {
-    private List<Content> content;
+  private List<Content> content;
+
+  @Data
+  public static class Content {
+    private String type;
+    private Text text;
 
     @Data
-    public static class Content {
-        private String type;
-        private Text text;
-
-        @Data
-        public static class Text {
-            private String value;
-        }
+    public static class Text {
+      private String value;
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptToolResources.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptToolResources.java
index fcf268c..f341de0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptToolResources.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateful/model/api/chatgpt/ChatGptToolResources.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt;
 
 import com.google.gson.annotations.SerializedName;
@@ -7,13 +21,13 @@
 @Data
 @AllArgsConstructor
 public class ChatGptToolResources {
-    @SerializedName("file_search")
-    private VectorStoreIds fileSearch;
+  @SerializedName("file_search")
+  private VectorStoreIds fileSearch;
 
-    @Data
-    @AllArgsConstructor
-    public static class VectorStoreIds {
-        @SerializedName("vector_store_ids")
-        private String[] vectorStoreIds;
-    }
-}
\ No newline at end of file
+  @Data
+  @AllArgsConstructor
+  public static class VectorStoreIds {
+    @SerializedName("vector_store_ids")
+    private String[] vectorStoreIds;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/UriResourceLocatorStateless.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/UriResourceLocatorStateless.java
index 1475dfc..5d022b5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/UriResourceLocatorStateless.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/UriResourceLocatorStateless.java
@@ -1,44 +1,62 @@
-package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api;
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
 
+package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api;
 
 import com.google.common.base.Strings;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 
 public class UriResourceLocatorStateless {
 
-    public static String getChatResourceUri(Configuration configuration) {
-        // different resource Uri endpoints exist for each aiType we support.
-        // some have backward compatible endpoints, but some do not.  This abstracts the knowledge
-        // of which to call for which aiType set in the configuration settings.
-        switch (configuration.getAIType()) {
-            case CHATGPT:
-            // ollama has an OpenAI compatible completions URI endpoint now,
-            // use so that the tools_calls format is used for returned data by default saving
-            // on loads more code changes in the response parsing.
-            case OLLAMA:
-                return chatCompletionsUri();
+  public static String getChatResourceUri(Configuration configuration) {
+    // different resource Uri endpoints exist for each aiType we support.
+    // some have backward compatible endpoints, but some do not.  This abstracts the knowledge
+    // of which to call for which aiType set in the configuration settings.
+    switch (configuration.getAIType()) {
+      case CHATGPT:
+      // ollama has an OpenAI compatible completions URI endpoint now,
+      // use so that the tools_calls format is used for returned data by default saving
+      // on loads more code changes in the response parsing.
+      case OLLAMA:
+        return chatCompletionsUri();
 
-            case AZUREOPENAI:
-                // TODO: add the endpoint information here, along with the additional query param with the version
-                // info - for testing use generic for now, but we will want to add the additional version info as another
-                // config element and then append the 2 together here.
-                throw new UnsupportedOperationException("AzureOpenAi endpoint not yet supported.");
-            case GENERIC:
-                // generic ai development will require you to override the endpoint if it doesn't support the existing
-                // chatCompletionsUri.. Usually you will provide the optional chatEndpoint configuration option.
-                final String chatEndpoint = configuration.getChatEndpoint();
-                // fallback onto chatCompletions api if nothing has been specified.
-                return Strings.isNullOrEmpty(chatEndpoint) ? chatCompletionsUri() : chatEndpoint;
-            default:
-                throw new UnsupportedOperationException("Unsupported aiType, chat resource endpoint not yet described.");
-        }
+      case AZUREOPENAI:
+        // TODO: add the endpoint information here, along with the additional query param with the
+        // version
+        // info - for testing use generic for now, but we will want to add the additional version
+        // info as another
+        // config element and then append the 2 together here.
+        throw new UnsupportedOperationException("AzureOpenAi endpoint not yet supported.");
+      case GENERIC:
+        // generic ai development will require you to override the endpoint if it doesn't support
+        // the existing
+        // chatCompletionsUri.. Usually you will provide the optional chatEndpoint configuration
+        // option.
+        final String chatEndpoint = configuration.getChatEndpoint();
+        // fallback onto chatCompletions api if nothing has been specified.
+        return Strings.isNullOrEmpty(chatEndpoint) ? chatCompletionsUri() : chatEndpoint;
+      default:
+        throw new UnsupportedOperationException(
+            "Unsupported aiType, chat resource endpoint not yet described.");
     }
+  }
 
-    public static String chatCompletionsUri() {
-        return "/v1/chat/completions";
-    }
+  public static String chatCompletionsUri() {
+    return "/v1/chat/completions";
+  }
 
-    public static String ollamaChatUri() {
-        return "/api/chat";
-    }
+  public static String ollamaChatUri() {
+    return "/api/chat";
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/chatai/AIChatClientStateless.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/chatai/AIChatClientStateless.java
index 36adbe5..3b782a4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/chatai/AIChatClientStateless.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/chatai/AIChatClientStateless.java
@@ -1,117 +1,141 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.chatai;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getNoEscapedGson;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.net.HttpHeaders;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.openapi.ChatAIClient;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatClient;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatParameters;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.openai.AIChatTools;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.http.HttpClientWithRetry;
-import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.*;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseContent;
+import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatTool;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.UriResourceLocatorStateless;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.prompt.AIChatPromptStateless;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.model.api.chatgpt.ChatGptCompletionRequest;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.http.NameValuePair;
-import org.apache.http.entity.ContentType;
-
 import java.io.IOException;
 import java.net.URI;
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
 import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getNoEscapedGson;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.NameValuePair;
+import org.apache.http.entity.ContentType;
 
 @Slf4j
 @Singleton
 public class AIChatClientStateless extends AIChatClient implements ChatAIClient {
-    private static final int REVIEW_ATTEMPT_LIMIT = 3;
+  private static final int REVIEW_ATTEMPT_LIMIT = 3;
 
-    private final HttpClientWithRetry httpClientWithRetry = new HttpClientWithRetry();
+  private final HttpClientWithRetry httpClientWithRetry = new HttpClientWithRetry();
 
-    @VisibleForTesting
-    @Inject
-    public AIChatClientStateless(Configuration config) {
-        super(config);
+  @VisibleForTesting
+  @Inject
+  public AIChatClientStateless(Configuration config) {
+    super(config);
+  }
+
+  public AIChatResponseContent ask(
+      ChangeSetData changeSetData, GerritChange change, String patchSet) throws Exception {
+    isCommentEvent = change.getIsCommentEvent();
+    String changeId = change.getFullChangeId();
+    log.info(
+        "Processing STATELESS ChatGPT Request with changeId: {}, Patch Set: {}",
+        changeId,
+        patchSet);
+    for (int attemptInd = 0; attemptInd < REVIEW_ATTEMPT_LIMIT; attemptInd++) {
+      HttpRequest request = createRequest(config, changeSetData, patchSet);
+      log.debug("ChatGPT request: {}", request.toString());
+
+      HttpResponse<String> response = httpClientWithRetry.execute(request);
+
+      String body = response.body();
+      log.debug("ChatGPT response body: {}", body);
+      if (body == null) {
+        throw new IOException("ChatGPT response body is null");
+      }
+
+      AIChatResponseContent contentExtracted = extractContent(config, body);
+      if (validateResponse(contentExtracted, changeId, attemptInd)) {
+        return contentExtracted;
+      }
     }
+    throw new RuntimeException("Failed to receive valid ChatGPT response");
+  }
 
-    public AIChatResponseContent ask(ChangeSetData changeSetData, GerritChange change, String patchSet)
-            throws Exception {
-        isCommentEvent = change.getIsCommentEvent();
-        String changeId = change.getFullChangeId();
-        log.info("Processing STATELESS ChatGPT Request with changeId: {}, Patch Set: {}", changeId, patchSet);
-        for (int attemptInd = 0; attemptInd < REVIEW_ATTEMPT_LIMIT; attemptInd++) {
-            HttpRequest request = createRequest(config, changeSetData, patchSet);
-            log.debug("ChatGPT request: {}", request.toString());
+  protected HttpRequest createRequest(
+      Configuration config, ChangeSetData changeSetData, String patchSet) {
+    URI uri =
+        URI.create(config.getAIDomain() + UriResourceLocatorStateless.getChatResourceUri(config));
+    log.debug("AIChat request URI: {}", uri);
+    requestBody = createRequestBody(config, changeSetData, patchSet);
+    log.debug("AIChat request body: {}", requestBody);
 
-            HttpResponse<String> response = httpClientWithRetry.execute(request);
+    HttpRequest.Builder builder =
+        HttpRequest.newBuilder()
+            .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+            .uri(uri)
+            .POST(HttpRequest.BodyPublishers.ofString(requestBody));
 
-            String body = response.body();
-            log.debug("ChatGPT response body: {}", body);
-            if (body == null) {
-                throw new IOException("ChatGPT response body is null");
-            }
-
-            AIChatResponseContent contentExtracted = extractContent(config, body);
-            if (validateResponse(contentExtracted, changeId, attemptInd)) {
-                return contentExtracted;
-            }
-        }
-        throw new RuntimeException("Failed to receive valid ChatGPT response");
+    // depending on the aiType, add appropriate authorization header ( if required ).
+    NameValuePair authHeader = config.getAuthorizationHeaderInfo();
+    if (authHeader != null) {
+      builder.header(authHeader.getName(), authHeader.getValue());
     }
+    return builder.build();
+  }
 
-    protected HttpRequest createRequest(Configuration config, ChangeSetData changeSetData, String patchSet) {
-        URI uri = URI.create(config.getAIDomain() + UriResourceLocatorStateless.getChatResourceUri(config));
-        log.debug("AIChat request URI: {}", uri);
-        requestBody = createRequestBody(config, changeSetData, patchSet);
-        log.debug("AIChat request body: {}", requestBody);
+  private String createRequestBody(
+      Configuration config, ChangeSetData changeSetData, String patchSet) {
+    AIChatPromptStateless AIChatPromptStateless = new AIChatPromptStateless(config, isCommentEvent);
+    AIChatRequestMessage systemMessage =
+        AIChatRequestMessage.builder()
+            .role("system")
+            .content(AIChatPromptStateless.getAISystemPrompt())
+            .build();
+    AIChatRequestMessage userMessage =
+        AIChatRequestMessage.builder()
+            .role("user")
+            .content(AIChatPromptStateless.getGptUserPrompt(changeSetData, patchSet))
+            .build();
 
-        HttpRequest.Builder builder = HttpRequest.newBuilder()
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                .uri(uri)
-                .POST(HttpRequest.BodyPublishers.ofString(requestBody));
+    AIChatParameters AIChatParameters = new AIChatParameters(config, isCommentEvent);
+    AIChatTool[] tools = new AIChatTool[] {AIChatTools.retrieveFormatRepliesTool()};
+    ChatGptCompletionRequest chatGptCompletionRequest =
+        ChatGptCompletionRequest.builder()
+            .model(config.getAIModel())
+            .messages(List.of(systemMessage, userMessage))
+            .temperature(AIChatParameters.getGptTemperature())
+            .stream(AIChatParameters.getStreamOutput())
+            // Seed value is Utilized to prevent ChatGPT from mixing up separate API calls that
+            // occur in close
+            // temporal proximity.
+            .seed(AIChatParameters.getRandomSeed())
+            .tools(tools)
+            .toolChoice(AIChatTools.retrieveFormatRepliesToolChoice())
+            .build();
 
-        // depending on the aiType, add appropriate authorization header ( if required ).
-        NameValuePair authHeader = config.getAuthorizationHeaderInfo();
-        if (authHeader != null) {
-            builder.header(authHeader.getName(), authHeader.getValue());
-        }
-        return builder.build();
-    }
-
-    private String createRequestBody(Configuration config, ChangeSetData changeSetData, String patchSet) {
-        AIChatPromptStateless AIChatPromptStateless = new AIChatPromptStateless(config, isCommentEvent);
-        AIChatRequestMessage systemMessage = AIChatRequestMessage.builder()
-                .role("system")
-                .content(AIChatPromptStateless.getAISystemPrompt())
-                .build();
-        AIChatRequestMessage userMessage = AIChatRequestMessage.builder()
-                .role("user")
-                .content(AIChatPromptStateless.getGptUserPrompt(changeSetData, patchSet))
-                .build();
-
-        AIChatParameters AIChatParameters = new AIChatParameters(config, isCommentEvent);
-        AIChatTool[] tools = new AIChatTool[]{
-                AIChatTools.retrieveFormatRepliesTool()
-        };
-        ChatGptCompletionRequest chatGptCompletionRequest = ChatGptCompletionRequest.builder()
-                .model(config.getAIModel())
-                .messages(List.of(systemMessage, userMessage))
-                .temperature(AIChatParameters.getGptTemperature())
-                .stream(AIChatParameters.getStreamOutput())
-                // Seed value is Utilized to prevent ChatGPT from mixing up separate API calls that occur in close
-                // temporal proximity.
-                .seed(AIChatParameters.getRandomSeed())
-                .tools(tools)
-                .toolChoice(AIChatTools.retrieveFormatRepliesToolChoice())
-                .build();
-
-        return getNoEscapedGson().toJson(chatGptCompletionRequest);
-    }
+    return getNoEscapedGson().toJson(chatGptCompletionRequest);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/gerrit/GerritClientPatchSetStateless.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/gerrit/GerritClientPatchSetStateless.java
index ad8e28c..dc7ad66 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/gerrit/GerritClientPatchSetStateless.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/api/gerrit/GerritClientPatchSetStateless.java
@@ -1,77 +1,95 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.gerrit;
 
-import com.google.inject.Inject;
+import static java.util.stream.Collectors.toList;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.extensions.common.FileInfo;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.gerrit.GerritClientPatchSet;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.extern.slf4j.Slf4j;
-
-import static java.util.stream.Collectors.toList;
-
 import java.util.List;
 import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
-public class GerritClientPatchSetStateless extends com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClientPatchSet implements GerritClientPatchSet {
-    @VisibleForTesting
-    @Inject
-    public GerritClientPatchSetStateless(Configuration config, AccountCache accountCache) {
-        super(config, accountCache);
+public class GerritClientPatchSetStateless
+    extends com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit
+        .GerritClientPatchSet
+    implements GerritClientPatchSet {
+  @VisibleForTesting
+  @Inject
+  public GerritClientPatchSetStateless(Configuration config, AccountCache accountCache) {
+    super(config, accountCache);
+  }
+
+  public String getPatchSet(ChangeSetData changeSetData, GerritChange change) throws Exception {
+    int revisionBase = getChangeSetRevisionBase(changeSetData);
+    log.debug("Revision base: {}", revisionBase);
+
+    List<String> files = getAffectedFiles(change, revisionBase);
+    log.debug("Patch files: {}", files);
+
+    String fileDiffsJson = getFileDiffsJson(change, files, revisionBase);
+    log.debug("File diffs: {}", fileDiffsJson);
+
+    return fileDiffsJson;
+  }
+
+  private List<String> getAffectedFiles(GerritChange change, int revisionBase) throws Exception {
+    try (ManualRequestContext requestContext = config.openRequestContext()) {
+      Map<String, FileInfo> files =
+          config
+              .getGerritApi()
+              .changes()
+              .id(
+                  change.getProjectName(),
+                  change.getBranchNameKey().shortName(),
+                  change.getChangeKey().get())
+              .current()
+              .files(revisionBase);
+      return files.entrySet().stream()
+          .filter(
+              fileEntry -> {
+                String filename = fileEntry.getKey();
+                if (!filename.equals("/COMMIT_MSG") || config.getAIReviewCommitMessages()) {
+                  if (fileEntry.getValue().size > config.getMaxReviewFileSize()) {
+                    log.info(
+                        "File '{}' not reviewed because its size exceeds the fixed maximum"
+                            + " allowable size.",
+                        filename);
+                  } else {
+                    return true;
+                  }
+                }
+                return false;
+              })
+          .map(Map.Entry::getKey)
+          .collect(toList());
     }
+  }
 
-    public String getPatchSet(ChangeSetData changeSetData, GerritChange change) throws Exception {
-        int revisionBase = getChangeSetRevisionBase(changeSetData);
-        log.debug("Revision base: {}", revisionBase);
-
-        List<String> files = getAffectedFiles(change, revisionBase);
-        log.debug("Patch files: {}", files);
-
-        String fileDiffsJson = getFileDiffsJson(change, files, revisionBase);
-        log.debug("File diffs: {}", fileDiffsJson);
-
-        return fileDiffsJson;
-    }
-
-    private List<String> getAffectedFiles(GerritChange change, int revisionBase) throws Exception {
-        try (ManualRequestContext requestContext = config.openRequestContext()) {
-            Map<String, FileInfo> files =
-                config
-                    .getGerritApi()
-                    .changes()
-                    .id(
-                        change.getProjectName(),
-                        change.getBranchNameKey().shortName(),
-                        change.getChangeKey().get())
-                    .current()
-                    .files(revisionBase);
-            return files.entrySet().stream()
-                .filter(
-                    fileEntry -> {
-                      String filename = fileEntry.getKey();
-                      if (!filename.equals("/COMMIT_MSG") || config.getAIReviewCommitMessages()) {
-                        if (fileEntry.getValue().size > config.getMaxReviewFileSize()) {
-                          log.info(
-                              "File '{}' not reviewed because its size exceeds the fixed maximum allowable size.",
-                              filename);
-                        } else {
-                          return true;
-                        }
-                      }
-                      return false;
-                    })
-                .map(Map.Entry::getKey)
-                .collect(toList());
-        }
-    }
-
-    private String getFileDiffsJson(GerritChange change, List<String> files, int revisionBase) throws Exception {
-        retrieveFileDiff(change, files, revisionBase);
-        diffs.add(String.format("{\"changeId\": \"%s\"}", change.getFullChangeId()));
-        return "[" + String.join(",", diffs) + "]\n";
-    }
+  private String getFileDiffsJson(GerritChange change, List<String> files, int revisionBase)
+      throws Exception {
+    retrieveFileDiff(change, files, revisionBase);
+    diffs.add(String.format("{\"changeId\": \"%s\"}", change.getFullChangeId()));
+    return "[" + String.join(",", diffs) + "]\n";
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatDataPromptRequestsStateless.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatDataPromptRequestsStateless.java
index 2011a3f..9166d74 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatDataPromptRequestsStateless.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatDataPromptRequestsStateless.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.prompt;
 
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
@@ -10,20 +24,20 @@
 import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
-public class AIChatDataPromptRequestsStateless extends AIChatDataPromptRequests implements ChatAIDataPrompt {
-    public AIChatDataPromptRequestsStateless(
-            Configuration config,
-            ChangeSetData changeSetData,
-            GerritClientData gerritClientData,
-            Localizer localizer
-    ) {
-        super(config, changeSetData, gerritClientData, localizer);
-    }
+public class AIChatDataPromptRequestsStateless extends AIChatDataPromptRequests
+    implements ChatAIDataPrompt {
+  public AIChatDataPromptRequestsStateless(
+      Configuration config,
+      ChangeSetData changeSetData,
+      GerritClientData gerritClientData,
+      Localizer localizer) {
+    super(config, changeSetData, gerritClientData, localizer);
+  }
 
-    protected AIChatMessageItem getMessageItem(int i) {
-        super.getMessageItem(i);
-        setHistory(messageItem, messageHistory);
+  protected AIChatMessageItem getMessageItem(int i) {
+    super.getMessageItem(i);
+    setHistory(messageItem, messageHistory);
 
-        return messageItem;
-    }
+    return messageItem;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatPromptStateless.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatPromptStateless.java
index e07ac2b..692d3a9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatPromptStateless.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/client/prompt/AIChatPromptStateless.java
@@ -1,103 +1,122 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.prompt;
 
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
+
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatGptPrompt;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.*;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class AIChatPromptStateless extends AIChatGptPrompt {
-    public static String DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION;
-    public static String DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW;
-    public static String DEFAULT_AI_CHAT_REVIEW_PROMPT;
-    public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_REVIEW;
-    public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_MESSAGE_HISTORY;
-    public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_DIFF;
+  public static String DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION;
+  public static String DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW;
+  public static String DEFAULT_AI_CHAT_REVIEW_PROMPT;
+  public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_REVIEW;
+  public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_MESSAGE_HISTORY;
+  public static String DEFAULT_AI_CHAT_REVIEW_PROMPT_DIFF;
 
-    public AIChatPromptStateless(Configuration config) {
-        super(config);
-        loadStatelessPrompts();
-    }
+  public AIChatPromptStateless(Configuration config) {
+    super(config);
+    loadStatelessPrompts();
+  }
 
-    public AIChatPromptStateless(Configuration config, boolean isCommentEvent) {
-        super(config, isCommentEvent);
-        loadStatelessPrompts();
-    }
+  public AIChatPromptStateless(Configuration config, boolean isCommentEvent) {
+    super(config, isCommentEvent);
+    loadStatelessPrompts();
+  }
 
-    public static String getDefaultGptReviewSystemPrompt() {
-        return joinWithSpace(new ArrayList<>(List.of(
+  public static String getDefaultGptReviewSystemPrompt() {
+    return joinWithSpace(
+        new ArrayList<>(
+            List.of(
                 DEFAULT_AI_CHAT_SYSTEM_PROMPT + DOT,
                 DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION,
-                DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW
-        )));
-    }
+                DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW)));
+  }
 
-    public String getAISystemPrompt() {
-        List<String> prompt = new ArrayList<>(Arrays.asList(
-                config.getString(Configuration.KEY_AI_SYSTEM_PROMPT, DEFAULT_AI_CHAT_SYSTEM_PROMPT) + DOT,
-                AIChatPromptStateless.DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION
-        ));
-        if (!isCommentEvent) {
-            prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW);
-        }
-        return joinWithSpace(prompt);
+  public String getAISystemPrompt() {
+    List<String> prompt =
+        new ArrayList<>(
+            Arrays.asList(
+                config.getString(Configuration.KEY_AI_SYSTEM_PROMPT, DEFAULT_AI_CHAT_SYSTEM_PROMPT)
+                    + DOT,
+                AIChatPromptStateless.DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION));
+    if (!isCommentEvent) {
+      prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION_REVIEW);
     }
+    return joinWithSpace(prompt);
+  }
 
-    public String getGptUserPrompt(ChangeSetData changeSetData, String patchSet) {
-        List<String> prompt = new ArrayList<>();
-        String gptRequestDataPrompt = changeSetData.getReviewAIDataPrompt();
-        boolean isValidRequestDataPrompt = gptRequestDataPrompt != null && !gptRequestDataPrompt.isEmpty();
-        if (isCommentEvent && isValidRequestDataPrompt) {
-            log.debug("Request User Prompt retrieved: {}", gptRequestDataPrompt);
-            prompt.addAll(Arrays.asList(
-                    DEFAULT_AI_CHAT_REQUEST_PROMPT_DIFF,
-                    patchSet,
-                    DEFAULT_AI_CHAT_REQUEST_PROMPT_REQUESTS,
-                    gptRequestDataPrompt,
-                    getCommentRequestPrompt(changeSetData.getCommentPropertiesSize())
-            ));
-        }
-        else {
-            prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT);
-            prompt.addAll(getReviewSteps());
-            prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_DIFF);
-            prompt.add(patchSet);
-            if (isValidRequestDataPrompt) {
-                prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_MESSAGE_HISTORY);
-                prompt.add(gptRequestDataPrompt);
-            }
-            if (!changeSetData.getDirectives().isEmpty()) {
-                prompt.add(DEFAULT_AI_CHAT_REVIEW_PROMPT_DIRECTIVES);
-                prompt.add(getNumberedListString(new ArrayList<>(changeSetData.getDirectives()), null, null));
-            }
-        }
-        return joinWithNewLine(prompt);
+  public String getGptUserPrompt(ChangeSetData changeSetData, String patchSet) {
+    List<String> prompt = new ArrayList<>();
+    String gptRequestDataPrompt = changeSetData.getReviewAIDataPrompt();
+    boolean isValidRequestDataPrompt =
+        gptRequestDataPrompt != null && !gptRequestDataPrompt.isEmpty();
+    if (isCommentEvent && isValidRequestDataPrompt) {
+      log.debug("Request User Prompt retrieved: {}", gptRequestDataPrompt);
+      prompt.addAll(
+          Arrays.asList(
+              DEFAULT_AI_CHAT_REQUEST_PROMPT_DIFF,
+              patchSet,
+              DEFAULT_AI_CHAT_REQUEST_PROMPT_REQUESTS,
+              gptRequestDataPrompt,
+              getCommentRequestPrompt(changeSetData.getCommentPropertiesSize())));
+    } else {
+      prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT);
+      prompt.addAll(getReviewSteps());
+      prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_DIFF);
+      prompt.add(patchSet);
+      if (isValidRequestDataPrompt) {
+        prompt.add(AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_MESSAGE_HISTORY);
+        prompt.add(gptRequestDataPrompt);
+      }
+      if (!changeSetData.getDirectives().isEmpty()) {
+        prompt.add(DEFAULT_AI_CHAT_REVIEW_PROMPT_DIRECTIVES);
+        prompt.add(
+            getNumberedListString(new ArrayList<>(changeSetData.getDirectives()), null, null));
+      }
     }
+    return joinWithNewLine(prompt);
+  }
 
-    private void loadStatelessPrompts() {
-        // Avoid repeated loading of prompt constants
-        if (DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION == null) {
-            loadDefaultPrompts("promptsStateless");
-        }
+  private void loadStatelessPrompts() {
+    // Avoid repeated loading of prompt constants
+    if (DEFAULT_AI_CHAT_SYSTEM_PROMPT_INPUT_DESCRIPTION == null) {
+      loadDefaultPrompts("promptsStateless");
     }
+  }
 
-    private List<String> getReviewSteps() {
-        List<String> steps = new ArrayList<>(List.of(
-                joinWithSpace(new ArrayList<>(List.of(
-                        DEFAULT_AI_CHAT_REVIEW_PROMPT_REVIEW,
-                        DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT,
-                        getPatchSetReviewPrompt()
-                )))
-        ));
-        if (config.getAIReviewCommitMessages()) {
-            steps.add(getReviewPromptCommitMessages());
-        }
-        return steps;
+  private List<String> getReviewSteps() {
+    List<String> steps =
+        new ArrayList<>(
+            List.of(
+                joinWithSpace(
+                    new ArrayList<>(
+                        List.of(
+                            DEFAULT_AI_CHAT_REVIEW_PROMPT_REVIEW,
+                            DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT,
+                            getPatchSetReviewPrompt())))));
+    if (config.getAIReviewCommitMessages()) {
+      steps.add(getReviewPromptCommitMessages());
     }
+    return steps;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/model/api/chatgpt/ChatGptCompletionRequest.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/model/api/chatgpt/ChatGptCompletionRequest.java
index a65e0a9..85d9abf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/model/api/chatgpt/ChatGptCompletionRequest.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/mode/stateless/model/api/chatgpt/ChatGptCompletionRequest.java
@@ -1,23 +1,37 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.mode.stateless.model.api.chatgpt;
 
 import com.google.gson.annotations.SerializedName;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatRequestMessage;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatTool;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatToolChoice;
+import java.util.List;
 import lombok.Builder;
 import lombok.Data;
 
-import java.util.List;
-
 @Data
 @Builder
 public class ChatGptCompletionRequest {
-    private String model;
-    private boolean stream;
-    private double temperature;
-    private int seed;
-    private List<AIChatRequestMessage> messages;
-    private AIChatTool[] tools;
-    @SerializedName("tool_choice")
-    private AIChatToolChoice toolChoice;
+  private String model;
+  private boolean stream;
+  private double temperature;
+  private int seed;
+  private List<AIChatRequestMessage> messages;
+  private AIChatTool[] tools;
+
+  @SerializedName("tool_choice")
+  private AIChatToolChoice toolChoice;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/settings/Settings.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/settings/Settings.java
index 00dc4c5..8c1ecfa 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/settings/Settings.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/settings/Settings.java
@@ -1,36 +1,52 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.settings;
 
 import java.util.Map;
 
 public class Settings {
-    public static final String GERRIT_PATCH_SET_FILENAME = "/PATCHSET_LEVEL";
-    public static final String GERRIT_DEFAULT_MESSAGE_DONE = "Done";
-    public static final String GERRIT_AUTOGENERATED_PREFIX = "autogenerated:";
-    public static final Map<String , String> COMMIT_MESSAGE_FILTER_OUT_PREFIXES = Map.of(
-            "PARENT","Parent:",
-            "AUTHOR","Author:",
-            "AUTHOR_DATE", "AuthorDate:",
-            "COMMIT","Commit:",
-            "COMMIT_DATE", "CommitDate:",
-            "CHANGE_ID", "Change-Id:"
-    );
-    public static final String GERRIT_COMMIT_MESSAGE_PREFIX = "Subject: ";
+  public static final String GERRIT_PATCH_SET_FILENAME = "/PATCHSET_LEVEL";
+  public static final String GERRIT_DEFAULT_MESSAGE_DONE = "Done";
+  public static final String GERRIT_AUTOGENERATED_PREFIX = "autogenerated:";
+  public static final Map<String, String> COMMIT_MESSAGE_FILTER_OUT_PREFIXES =
+      Map.of(
+          "PARENT", "Parent:",
+          "AUTHOR", "Author:",
+          "AUTHOR_DATE", "AuthorDate:",
+          "COMMIT", "Commit:",
+          "COMMIT_DATE", "CommitDate:",
+          "CHANGE_ID", "Change-Id:");
+  public static final String GERRIT_COMMIT_MESSAGE_PREFIX = "Subject: ";
 
-    public static final String OPEN_AI_CHAT_ROLE_USER = "user";
-    public static final String OPEN_AI_CHAT_ROLE_ASSISTANT = "assistant";
+  public static final String OPEN_AI_CHAT_ROLE_USER = "user";
+  public static final String OPEN_AI_CHAT_ROLE_ASSISTANT = "assistant";
 
-    public enum Modes {
-        stateless,
-        stateful
-    }
+  public enum Modes {
+    stateless,
+    stateful
+  }
 
-    public enum AIType {
-        CHATGPT,
-        OLLAMA,
-        AZUREOPENAI,
+  public enum AIType {
+    CHATGPT,
+    OLLAMA,
+    AZUREOPENAI,
 
-        // used for testing new endpoints and ai services not yet in the supported list, but you can specify the
-        // endpoint, the authorization header information, and the prompt to quickly prove out a new aiType.
-        GENERIC;
-    }
+    // used for testing new endpoints and ai services not yet in the supported list, but you can
+    // specify the
+    // endpoint, the authorization header information, and the prompt to quickly prove out a new
+    // aiType.
+    GENERIC;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/FileUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/FileUtils.java
index bad1db3..d236f79 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/FileUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/FileUtils.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
 import java.io.IOException;
@@ -9,31 +23,32 @@
 import java.util.Objects;
 
 public class FileUtils {
-    public static InputStreamReader getInputStreamReader(String filename) {
-        return new InputStreamReader(Objects.requireNonNull(
-                FileUtils.class.getClassLoader().getResourceAsStream(filename)), StandardCharsets.UTF_8);
-    }
+  public static InputStreamReader getInputStreamReader(String filename) {
+    return new InputStreamReader(
+        Objects.requireNonNull(FileUtils.class.getClassLoader().getResourceAsStream(filename)),
+        StandardCharsets.UTF_8);
+  }
 
-    public static Path createTempFileWithContent(String prefix, String suffix, String content) {
-        Path tempFile;
-        try {
-            tempFile = Files.createTempFile(prefix, suffix);
-            Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        tempFile.toFile().deleteOnExit();
-
-        return tempFile;
+  public static Path createTempFileWithContent(String prefix, String suffix, String content) {
+    Path tempFile;
+    try {
+      tempFile = Files.createTempFile(prefix, suffix);
+      Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
     }
+    tempFile.toFile().deleteOnExit();
 
-    public static boolean matchesExtensionList(String filename, List<String> extensions) {
-        int extIndex = filename.lastIndexOf('.');
-        return extIndex >= 1 && extensions.contains(filename.substring(extIndex));
-    }
+    return tempFile;
+  }
 
-    public static String sanitizeFilename (String filename) {
-        // Replace any characters that are invalid in filenames (especially slashes) with a "+"
-        return filename.replaceAll("[^-_a-zA-Z0-9]", "+");
-    }
+  public static boolean matchesExtensionList(String filename, List<String> extensions) {
+    int extIndex = filename.lastIndexOf('.');
+    return extIndex >= 1 && extensions.contains(filename.substring(extIndex));
+  }
+
+  public static String sanitizeFilename(String filename) {
+    // Replace any characters that are invalid in filenames (especially slashes) with a "+"
+    return filename.replaceAll("[^-_a-zA-Z0-9]", "+");
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/GsonUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/GsonUtils.java
index 82d665c..9b7e8eb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/GsonUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/GsonUtils.java
@@ -1,16 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 
 public class GsonUtils {
-    public static Gson getGson() {
-        return new Gson();
-    }
+  public static Gson getGson() {
+    return new Gson();
+  }
 
-    public static Gson getNoEscapedGson() {
-        return new GsonBuilder()
-                .disableHtmlEscaping()
-                .create();
-    }
+  public static Gson getNoEscapedGson() {
+    return new GsonBuilder().disableHtmlEscaping().create();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/HashUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/HashUtils.java
index 4c09f76..20e168c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/HashUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/HashUtils.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
 import java.nio.charset.StandardCharsets;
@@ -7,33 +21,33 @@
 
 public class HashUtils {
 
-    public static String hashData(List<String> dataItems) {
-        StringBuilder concatenatedData = new StringBuilder();
-        for (String item : dataItems) {
-            concatenatedData.append(item);
-        }
-        return sha1(concatenatedData.toString());
+  public static String hashData(List<String> dataItems) {
+    StringBuilder concatenatedData = new StringBuilder();
+    for (String item : dataItems) {
+      concatenatedData.append(item);
     }
+    return sha1(concatenatedData.toString());
+  }
 
-    private static String sha1(String data) {
-        try {
-            MessageDigest digest = MessageDigest.getInstance("SHA-1");
-            byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
-            return bytesToHex(hashBytes);
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("SHA-1 algorithm not found", e);
-        }
+  private static String sha1(String data) {
+    try {
+      MessageDigest digest = MessageDigest.getInstance("SHA-1");
+      byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
+      return bytesToHex(hashBytes);
+    } catch (NoSuchAlgorithmException e) {
+      throw new RuntimeException("SHA-1 algorithm not found", e);
     }
+  }
 
-    private static String bytesToHex(byte[] hashBytes) {
-        StringBuilder hexString = new StringBuilder(2 * hashBytes.length);
-        for (byte b : hashBytes) {
-            String hex = Integer.toHexString(0xff & b);
-            if (hex.length() == 1) {
-                hexString.append('0');
-            }
-            hexString.append(hex);
-        }
-        return hexString.toString();
+  private static String bytesToHex(byte[] hashBytes) {
+    StringBuilder hexString = new StringBuilder(2 * hashBytes.length);
+    for (byte b : hashBytes) {
+      String hex = Integer.toHexString(0xff & b);
+      if (hex.length() == 1) {
+        hexString.append('0');
+      }
+      hexString.append(hex);
     }
+    return hexString.toString();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/JsonTextUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/JsonTextUtils.java
index d1b35a8..415ea4c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/JsonTextUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/JsonTextUtils.java
@@ -1,20 +1,34 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.regex.Pattern;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class JsonTextUtils extends TextUtils {
-    private static final Pattern JSON_DELIMITED = Pattern.compile("^.*?" + CODE_DELIMITER + "json\\s*(.*)\\s*" +
-                    CODE_DELIMITER + ".*$", Pattern.DOTALL);
-    private static final Pattern JSON_OBJECT = Pattern.compile("^\\{.*\\}$", Pattern.DOTALL);
+  private static final Pattern JSON_DELIMITED =
+      Pattern.compile(
+          "^.*?" + CODE_DELIMITER + "json\\s*(.*)\\s*" + CODE_DELIMITER + ".*$", Pattern.DOTALL);
+  private static final Pattern JSON_OBJECT = Pattern.compile("^\\{.*\\}$", Pattern.DOTALL);
 
-    public static String unwrapJsonCode(String text) {
-        return JSON_DELIMITED.matcher(text).replaceAll("$1");
-    }
+  public static String unwrapJsonCode(String text) {
+    return JSON_DELIMITED.matcher(text).replaceAll("$1");
+  }
 
-    public static boolean isJsonString(String text) {
-        return JSON_OBJECT.matcher(text).matches() || JSON_DELIMITED.matcher(text).matches();
-    }
+  public static boolean isJsonString(String text) {
+    return JSON_OBJECT.matcher(text).matches() || JSON_DELIMITED.matcher(text).matches();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/StringUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/StringUtils.java
index dd7a9c0..4e8fe43 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/StringUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/StringUtils.java
@@ -1,28 +1,41 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
-import lombok.extern.slf4j.Slf4j;
-
 import java.util.List;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class StringUtils {
-    public static String backslashEachChar(String body) {
-        StringBuilder slashedBody = new StringBuilder();
+  public static String backslashEachChar(String body) {
+    StringBuilder slashedBody = new StringBuilder();
 
-        for (char ch : body.toCharArray()) {
-            slashedBody.append("\\\\").append(ch);
-        }
-        return slashedBody.toString();
+    for (char ch : body.toCharArray()) {
+      slashedBody.append("\\\\").append(ch);
     }
+    return slashedBody.toString();
+  }
 
-    public static String concatenate(List<String> components) {
-        return String.join("", components);
-    }
+  public static String concatenate(List<String> components) {
+    return String.join("", components);
+  }
 
-    public static String capitalizeFirstLetter(String str) {
-        if (str == null || str.isEmpty()) {
-            return str;
-        }
-        return str.substring(0, 1).toUpperCase() + str.substring(1);
+  public static String capitalizeFirstLetter(String str) {
+    if (str == null || str.isEmpty()) {
+      return str;
     }
+    return str.substring(0, 1).toUpperCase() + str.substring(1);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TextUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TextUtils.java
index f70b4a4..7a65657 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TextUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TextUtils.java
@@ -1,98 +1,122 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
-import lombok.extern.slf4j.Slf4j;
-
 import java.lang.reflect.Field;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
+import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
 public class TextUtils extends StringUtils {
-    public static final String INLINE_CODE_DELIMITER = "`";
-    public static final String CODE_DELIMITER = "```";
-    public static final String CODE_DELIMITER_BEGIN ="\n\n" + CODE_DELIMITER + "\n";
-    public static final String CODE_DELIMITER_END ="\n" + CODE_DELIMITER + "\n";
-    public static final String SPACE = " ";
-    public static final String DOT = ".";
-    public static final String COMMA_SPACE = ", ";
-    public static final String COLON_SPACE = ": ";
-    public static final String SEMICOLON_SPACE = "; ";
+  public static final String INLINE_CODE_DELIMITER = "`";
+  public static final String CODE_DELIMITER = "```";
+  public static final String CODE_DELIMITER_BEGIN = "\n\n" + CODE_DELIMITER + "\n";
+  public static final String CODE_DELIMITER_END = "\n" + CODE_DELIMITER + "\n";
+  public static final String SPACE = " ";
+  public static final String DOT = ".";
+  public static final String COMMA_SPACE = ", ";
+  public static final String COLON_SPACE = ": ";
+  public static final String SEMICOLON_SPACE = "; ";
 
-    public static String parseOutOfDelimiters(String body, String splitDelim, Function<String, String> processMessage,
-                                              String leftDelimReplacement, String rightDelimReplacement) {
-        String[] chunks = body.split(splitDelim, -1);
-        List<String> resultChunks = new ArrayList<>();
-        int lastChunk = chunks.length -1;
-        for (int i = 0; i <= lastChunk; i++) {
-            String chunk = chunks[i];
-            if (i % 2 == 0 || i == lastChunk) {
-                resultChunks.add(processMessage.apply(chunk));
-            }
-            else {
-                resultChunks.addAll(Arrays.asList(leftDelimReplacement, chunk, rightDelimReplacement));
-            }
-        }
-        return concatenate(resultChunks);
+  public static String parseOutOfDelimiters(
+      String body,
+      String splitDelim,
+      Function<String, String> processMessage,
+      String leftDelimReplacement,
+      String rightDelimReplacement) {
+    String[] chunks = body.split(splitDelim, -1);
+    List<String> resultChunks = new ArrayList<>();
+    int lastChunk = chunks.length - 1;
+    for (int i = 0; i <= lastChunk; i++) {
+      String chunk = chunks[i];
+      if (i % 2 == 0 || i == lastChunk) {
+        resultChunks.add(processMessage.apply(chunk));
+      } else {
+        resultChunks.addAll(Arrays.asList(leftDelimReplacement, chunk, rightDelimReplacement));
+      }
     }
+    return concatenate(resultChunks);
+  }
 
-    public static String parseOutOfDelimiters(String body, String splitDelim, Function<String, String> processMessage) {
-        return parseOutOfDelimiters(body, splitDelim, processMessage, splitDelim, splitDelim);
-    }
+  public static String parseOutOfDelimiters(
+      String body, String splitDelim, Function<String, String> processMessage) {
+    return parseOutOfDelimiters(body, splitDelim, processMessage, splitDelim, splitDelim);
+  }
 
-    public static String joinWithNewLine(List<String> components) {
-        return String.join("\n", components);
-    }
+  public static String joinWithNewLine(List<String> components) {
+    return String.join("\n", components);
+  }
 
-    public static String joinWithDoubleNewLine(List<String> components) {
-        return String.join("\n\n", components);
-    }
+  public static String joinWithDoubleNewLine(List<String> components) {
+    return String.join("\n\n", components);
+  }
 
-    public static String joinWithSpace(List<String> components) {
-        return String.join(SPACE, components);
-    }
+  public static String joinWithSpace(List<String> components) {
+    return String.join(SPACE, components);
+  }
 
-    public static String joinWithComma(Set<String> components) {
-        return String.join(COMMA_SPACE, components);
-    }
+  public static String joinWithComma(Set<String> components) {
+    return String.join(COMMA_SPACE, components);
+  }
 
-    public static String joinWithSemicolon(List<String> components) {
-        return String.join(SEMICOLON_SPACE, components);
-    }
+  public static String joinWithSemicolon(List<String> components) {
+    return String.join(SEMICOLON_SPACE, components);
+  }
 
-    public static List<String> getNumberedList(List<String> components, String prefix, String postfix) {
-        return IntStream.range(0, components.size())
-                .mapToObj(i -> Optional.ofNullable(prefix).orElse("") +
-                        (i + 1) +
-                        Optional.ofNullable(postfix).orElse(". ") +
-                        components.get(i)
-                )
-                .collect(Collectors.toList());
-    }
+  public static List<String> getNumberedList(
+      List<String> components, String prefix, String postfix) {
+    return IntStream.range(0, components.size())
+        .mapToObj(
+            i ->
+                Optional.ofNullable(prefix).orElse("")
+                    + (i + 1)
+                    + Optional.ofNullable(postfix).orElse(". ")
+                    + components.get(i))
+        .collect(Collectors.toList());
+  }
 
-    public static String getNumberedListString(List<String> components, String prefix, String postfix) {
-        return joinWithSemicolon(getNumberedList(components, prefix, postfix));
-    }
+  public static String getNumberedListString(
+      List<String> components, String prefix, String postfix) {
+    return joinWithSemicolon(getNumberedList(components, prefix, postfix));
+  }
 
-    public static String prettyStringifyObject(Object object) {
-        List<String> lines = new ArrayList<>();
-        for (Field field : object.getClass().getDeclaredFields()) {
-            field.setAccessible(true);
-            try {
-                lines.add(field.getName() + ": " + field.get(object));
-            } catch (IllegalAccessException e) {
-                log.debug("Error while accessing field {} in {}", field.getName(), object, e);
-            }
-        }
-        return joinWithNewLine(lines);
+  public static String prettyStringifyObject(Object object) {
+    List<String> lines = new ArrayList<>();
+    for (Field field : object.getClass().getDeclaredFields()) {
+      field.setAccessible(true);
+      try {
+        lines.add(field.getName() + ": " + field.get(object));
+      } catch (IllegalAccessException e) {
+        log.debug("Error while accessing field {} in {}", field.getName(), object, e);
+      }
     }
+    return joinWithNewLine(lines);
+  }
 
-    public static String prettyStringifyMap(Map<String, String> map) {
-        return joinWithNewLine(
-                map.entrySet().stream()
-                        .map(entry -> entry.getKey() + ": " + entry.getValue())
-                        .collect(Collectors.toList())
-        );
-    }
+  public static String prettyStringifyMap(Map<String, String> map) {
+    return joinWithNewLine(
+        map.entrySet().stream()
+            .map(entry -> entry.getKey() + ": " + entry.getValue())
+            .collect(Collectors.toList()));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/ThreadUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/ThreadUtils.java
index 6ee8aac..f549d38 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/ThreadUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/ThreadUtils.java
@@ -1,12 +1,26 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
 public class ThreadUtils {
-    public static void threadSleep(long millis) {
-        try {
-            Thread.sleep(millis);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new RuntimeException("Thread was interrupted", e);
-        }
+  public static void threadSleep(long millis) {
+    try {
+      Thread.sleep(millis);
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      throw new RuntimeException("Thread was interrupted", e);
     }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TimeUtils.java b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TimeUtils.java
index 8d7b0c5..ac0be6d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TimeUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/aicodereview/utils/TimeUtils.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.utils;
 
 import java.time.LocalDateTime;
@@ -5,9 +19,9 @@
 import java.time.format.DateTimeFormatter;
 
 public class TimeUtils {
-    public static long getTimeStamp(String updatedString) {
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
-        LocalDateTime updatedDateTime = LocalDateTime.parse(updatedString, formatter);
-        return updatedDateTime.toInstant(ZoneOffset.UTC).getEpochSecond();
-    }
+  public static long getTimeStamp(String updatedString) {
+    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
+    LocalDateTime updatedDateTime = LocalDateTime.parse(updatedString, formatter);
+    return updatedDateTime.toInstant(ZoneOffset.UTC).getEpochSecond();
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatefulTest.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatefulTest.java
index b31b9f7..51b209d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatefulTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatefulTest.java
@@ -1,5 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
+import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatPromptFactory.getAIChatPromptStateful;
+import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt.ChatGptRun.COMPLETED_STATUS;
+import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt.ChatGptVectorStore.KEY_VECTOR_STORE_ID;
+import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_PATCH_SET_FILENAME;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
 import com.github.tomakehurst.wiremock.client.WireMock;
 import com.google.common.net.HttpHeaders;
 import com.google.gerrit.extensions.api.changes.FileApi;
@@ -10,11 +33,14 @@
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.stateful.client.prompt.ChatGptPromptStateful;
+import com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.SupportedEvents;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.api.openai.AIChatResponseContent;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.UriResourceLocatorStateful;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.model.api.chatgpt.ChatGptListResponse;
 import com.googlesource.gerrit.plugins.aicodereview.settings.Settings.Modes;
 import com.googlesource.gerrit.plugins.aicodereview.utils.ThreadUtils;
+import java.io.ByteArrayInputStream;
+import java.net.URI;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.entity.ContentType;
 import org.junit.Assert;
@@ -26,274 +52,351 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.io.ByteArrayInputStream;
-import java.net.URI;
-
-import static com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.SupportedEvents;
-import static com.googlesource.gerrit.plugins.aicodereview.mode.common.client.prompt.AIChatPromptFactory.getAIChatPromptStateful;
-import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt.ChatGptRun.COMPLETED_STATUS;
-import static com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.chatgpt.ChatGptVectorStore.KEY_VECTOR_STORE_ID;
-import static com.googlesource.gerrit.plugins.aicodereview.settings.Settings.GERRIT_PATCH_SET_FILENAME;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-import static java.net.HttpURLConnection.HTTP_OK;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
 @Slf4j
 @RunWith(MockitoJUnitRunner.class)
 public class AIChatReviewStatefulTest extends AIChatReviewTestBase {
-    private static final String AI_CHAT_FILE_ID = "file-TEST_FILE_ID";
-    private static final String AI_CHAT_VECTOR_ID = "file-TEST_VECTOR_ID";
-    private static final String AI_CHAT_ASSISTANT_ID = "asst_TEST_ASSISTANT_ID";
-    private static final String AI_CHAT_THREAD_ID = "thread_TEST_THREAD_ID";
-    private static final String AI_CHAT_MESSAGE_ID = "msg_TEST_MESSAGE_ID";
-    private static final String AI_CHAT_RUN_ID = "run_TEST_RUN_ID";
+  private static final String AI_CHAT_FILE_ID = "file-TEST_FILE_ID";
+  private static final String AI_CHAT_VECTOR_ID = "file-TEST_VECTOR_ID";
+  private static final String AI_CHAT_ASSISTANT_ID = "asst_TEST_ASSISTANT_ID";
+  private static final String AI_CHAT_THREAD_ID = "thread_TEST_THREAD_ID";
+  private static final String AI_CHAT_MESSAGE_ID = "msg_TEST_MESSAGE_ID";
+  private static final String AI_CHAT_RUN_ID = "run_TEST_RUN_ID";
 
-    private String formattedPatchContent;
-    private ChatGptPromptStateful chatGptPromptStateful;
-    private String requestContent;
-    private PluginDataHandler projectHandler;
+  private String formattedPatchContent;
+  private ChatGptPromptStateful chatGptPromptStateful;
+  private String requestContent;
+  private PluginDataHandler projectHandler;
 
-    public AIChatReviewStatefulTest() {
-        MockitoAnnotations.openMocks(this);
-    }
+  public AIChatReviewStatefulTest() {
+    MockitoAnnotations.openMocks(this);
+  }
 
-    protected void initGlobalAndProjectConfig() {
-        super.initGlobalAndProjectConfig();
+  protected void initGlobalAndProjectConfig() {
+    super.initGlobalAndProjectConfig();
 
-        // Mock the Global Config values that differ from the ones provided by Default
-        when(globalConfig.getString(Mockito.eq("aiMode"), Mockito.anyString()))
-                .thenReturn(Modes.stateful.name());
+    // Mock the Global Config values that differ from the ones provided by Default
+    when(globalConfig.getString(Mockito.eq("aiMode"), Mockito.anyString()))
+        .thenReturn(Modes.stateful.name());
 
-        setupPluginData();
-        PluginDataHandlerProvider provider = new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
-        projectHandler = provider.getProjectScope();
-        // Mock the pluginDataHandlerProvider to return the mocked project pluginDataHandler
-        when(pluginDataHandlerProvider.getProjectScope()).thenReturn(projectHandler);
-        // Mock the pluginDataHandlerProvider to return the mocked assistant pluginDataHandler
-        when(pluginDataHandlerProvider.getAssistantsWorkspace()).thenReturn(projectHandler);
-    }
+    setupPluginData();
+    PluginDataHandlerProvider provider =
+        new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
+    projectHandler = provider.getProjectScope();
+    // Mock the pluginDataHandlerProvider to return the mocked project pluginDataHandler
+    when(pluginDataHandlerProvider.getProjectScope()).thenReturn(projectHandler);
+    // Mock the pluginDataHandlerProvider to return the mocked assistant pluginDataHandler
+    when(pluginDataHandlerProvider.getAssistantsWorkspace()).thenReturn(projectHandler);
+  }
 
-    protected void initTest() {
-        super.initTest();
+  protected void initTest() {
+    super.initTest();
 
-        // Load the prompts
-        chatGptPromptStateful = getAIChatPromptStateful(config, changeSetData, getGerritChange());
-    }
+    // Load the prompts
+    chatGptPromptStateful = getAIChatPromptStateful(config, changeSetData, getGerritChange());
+  }
 
-    protected void setupMockRequests() throws RestApiException {
-        super.setupMockRequests();
+  protected void setupMockRequests() throws RestApiException {
+    super.setupMockRequests();
 
-        // Mock the behavior of the Git Repository Manager
-        String repoJson = readTestFile("__files/stateful/gitProjectFiles.json");
-        when(gitRepoFiles.getGitRepoFiles(any(), any())).thenReturn(repoJson);
+    // Mock the behavior of the Git Repository Manager
+    String repoJson = readTestFile("__files/stateful/gitProjectFiles.json");
+    when(gitRepoFiles.getGitRepoFiles(any(), any())).thenReturn(repoJson);
 
-        // Mock the behavior of the ChatGPT create-file request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.filesCreateUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"id\": " + AI_CHAT_FILE_ID + "}")));
+    // Mock the behavior of the ChatGPT create-file request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(config.getAIDomain() + UriResourceLocatorStateful.filesCreateUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"id\": " + AI_CHAT_FILE_ID + "}")));
 
-        // Mock the behavior of the ChatGPT create-vector-store request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.vectorStoreCreateUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"id\": " + AI_CHAT_VECTOR_ID + "}")));
+    // Mock the behavior of the ChatGPT create-vector-store request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.vectorStoreCreateUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"id\": " + AI_CHAT_VECTOR_ID + "}")));
 
-        // Mock the behavior of the ChatGPT create-assistant request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.assistantCreateUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"id\": " + AI_CHAT_ASSISTANT_ID + "}")));
+    // Mock the behavior of the ChatGPT create-assistant request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain() + UriResourceLocatorStateful.assistantCreateUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"id\": " + AI_CHAT_ASSISTANT_ID + "}")));
 
-        // Mock the behavior of the ChatGPT create-thread request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.threadsUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"id\": " + AI_CHAT_THREAD_ID + "}")));
+    // Mock the behavior of the ChatGPT create-thread request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(config.getAIDomain() + UriResourceLocatorStateful.threadsUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"id\": " + AI_CHAT_THREAD_ID + "}")));
 
-        // Mock the behavior of the ChatGPT add-message-to-thread request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.threadMessagesUri(AI_CHAT_THREAD_ID)).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"id\": " + AI_CHAT_MESSAGE_ID + "}")));
+    // Mock the behavior of the ChatGPT add-message-to-thread request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.threadMessagesUri(AI_CHAT_THREAD_ID))
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"id\": " + AI_CHAT_MESSAGE_ID + "}")));
 
-        // Mock the behavior of the ChatGPT create-run request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.runsUri(AI_CHAT_THREAD_ID)).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"id\": " + AI_CHAT_RUN_ID + "}")));
+    // Mock the behavior of the ChatGPT create-run request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.runsUri(AI_CHAT_THREAD_ID))
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"id\": " + AI_CHAT_RUN_ID + "}")));
 
-        // Mock the behavior of the ChatGPT retrieve-run request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.runRetrieveUri(AI_CHAT_THREAD_ID, AI_CHAT_RUN_ID)).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBody("{\"status\": " + COMPLETED_STATUS + "}")));
+    // Mock the behavior of the ChatGPT retrieve-run request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.runRetrieveUri(
+                                    AI_CHAT_THREAD_ID, AI_CHAT_RUN_ID))
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBody("{\"status\": " + COMPLETED_STATUS + "}")));
 
-        mockRetrieveRunSteps("chatGptRunStepsResponse.json");
+    mockRetrieveRunSteps("chatGptRunStepsResponse.json");
 
-        // Mock the behavior of the formatted patch request
-        formattedPatchContent = readTestFile("__files/stateful/gerritFormattedPatch.txt");
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(formattedPatchContent.getBytes());
-        BinaryResult binaryResult = BinaryResult.create(inputStream)
-                .setContentType("text/plain")
-                .setContentLength(formattedPatchContent.length());
-        when(revisionApiMock.patch()).thenReturn(binaryResult);
+    // Mock the behavior of the formatted patch request
+    formattedPatchContent = readTestFile("__files/stateful/gerritFormattedPatch.txt");
+    ByteArrayInputStream inputStream = new ByteArrayInputStream(formattedPatchContent.getBytes());
+    BinaryResult binaryResult =
+        BinaryResult.create(inputStream)
+            .setContentType("text/plain")
+            .setContentLength(formattedPatchContent.length());
+    when(revisionApiMock.patch()).thenReturn(binaryResult);
 
-        FileApi testFileMock = mock(FileApi.class);
-        when(revisionApiMock.file("test_file_1.py")).thenReturn(testFileMock);
-        DiffInfo testFileDiff = readTestFileToClass("__files/stateful/gerritPatchSetDiffTestFile.json", DiffInfo.class);
-        when(testFileMock.diff(0)).thenReturn(testFileDiff);
-    }
+    FileApi testFileMock = mock(FileApi.class);
+    when(revisionApiMock.file("test_file_1.py")).thenReturn(testFileMock);
+    DiffInfo testFileDiff =
+        readTestFileToClass("__files/stateful/gerritPatchSetDiffTestFile.json", DiffInfo.class);
+    when(testFileMock.diff(0)).thenReturn(testFileDiff);
+  }
 
-    protected void initComparisonContent() {
-        super.initComparisonContent();
+  protected void initComparisonContent() {
+    super.initComparisonContent();
 
-        promptTagComments = readTestFile("__files/stateful/aiChatPromptTagRequests.json");
-    }
+    promptTagComments = readTestFile("__files/stateful/aiChatPromptTagRequests.json");
+  }
 
-    protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
-        ArgumentCaptor<ReviewInput> reviewInputCaptor = super.testRequestSent();
-        requestContent = gptRequestBody.getAsJsonObject().get("content").getAsString();
-        return reviewInputCaptor;
-    }
+  protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
+    ArgumentCaptor<ReviewInput> reviewInputCaptor = super.testRequestSent();
+    requestContent = gptRequestBody.getAsJsonObject().get("content").getAsString();
+    return reviewInputCaptor;
+  }
 
-    private String getReviewMessage(String responseFile, int tollCallId) {
-        ChatGptListResponse responseContent = getGson().fromJson(readTestFile(responseFile), ChatGptListResponse.class);
-        String reviewJsonResponse = responseContent.getData().get(0).getStepDetails().getToolCalls().get(tollCallId)
-                .getFunction().getArguments();
-        return getGson().fromJson(reviewJsonResponse, AIChatResponseContent.class).getReplies().get(0).getReply();
-    }
+  private String getReviewMessage(String responseFile, int tollCallId) {
+    ChatGptListResponse responseContent =
+        getGson().fromJson(readTestFile(responseFile), ChatGptListResponse.class);
+    String reviewJsonResponse =
+        responseContent
+            .getData()
+            .get(0)
+            .getStepDetails()
+            .getToolCalls()
+            .get(tollCallId)
+            .getFunction()
+            .getArguments();
+    return getGson()
+        .fromJson(reviewJsonResponse, AIChatResponseContent.class)
+        .getReplies()
+        .get(0)
+        .getReply();
+  }
 
-    private String getCapturedMessage(ArgumentCaptor<ReviewInput> captor, String filename) {
-        return captor.getAllValues().get(0).comments.get(filename).get(0).message;
-    }
+  private String getCapturedMessage(ArgumentCaptor<ReviewInput> captor, String filename) {
+    return captor.getAllValues().get(0).comments.get(filename).get(0).message;
+  }
 
-    private void mockRetrieveRunSteps(String bodyFile) {
-        // Mock the behavior of the ChatGPT retrieve-run-steps request
-        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.runStepsUri(AI_CHAT_THREAD_ID, AI_CHAT_RUN_ID)).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBodyFile(bodyFile)));
-    }
+  private void mockRetrieveRunSteps(String bodyFile) {
+    // Mock the behavior of the ChatGPT retrieve-run-steps request
+    WireMock.stubFor(
+        WireMock.get(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.runStepsUri(
+                                    AI_CHAT_THREAD_ID, AI_CHAT_RUN_ID))
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBodyFile(bodyFile)));
+  }
 
-    @Test
-    public void patchSetCreatedOrUpdated() throws Exception {
-        String reviewMessageCode = getReviewMessage( "__files/chatGptRunStepsResponse.json", 0);
-        String reviewMessageCommitMessage = getReviewMessage( "__files/chatGptRunStepsResponse.json", 1);
+  @Test
+  public void patchSetCreatedOrUpdated() throws Exception {
+    String reviewMessageCode = getReviewMessage("__files/chatGptRunStepsResponse.json", 0);
+    String reviewMessageCommitMessage = getReviewMessage("__files/chatGptRunStepsResponse.json", 1);
 
-        String reviewPrompt = chatGptPromptStateful.getDefaultGptThreadReviewMessage(formattedPatchContent);
+    String reviewPrompt =
+        chatGptPromptStateful.getDefaultGptThreadReviewMessage(formattedPatchContent);
 
-        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
+    handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
-        ArgumentCaptor<ReviewInput> captor = testRequestSent();
-        Assert.assertEquals(reviewPrompt, requestContent);
-        Assert.assertEquals(reviewMessageCode, getCapturedMessage(captor, "test_file_1.py"));
-        Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
-    }
+    ArgumentCaptor<ReviewInput> captor = testRequestSent();
+    Assert.assertEquals(reviewPrompt, requestContent);
+    Assert.assertEquals(reviewMessageCode, getCapturedMessage(captor, "test_file_1.py"));
+    Assert.assertEquals(
+        reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
+  }
 
-    @Test
-    public void initialEmptyResponse() throws Exception {
-        // To effectively test how an initial empty response from ChatGPT is managed, the following approach is adopted:
-        // 1. the ChatGPT run-steps request is initially mocked to return an empty data field, and
-        // 2. the sleep function is mocked to replace the empty response with a valid one, instead of pausing execution
-        mockRetrieveRunSteps("chatGptRunStepsEmptyResponse.json");
+  @Test
+  public void initialEmptyResponse() throws Exception {
+    // To effectively test how an initial empty response from ChatGPT is managed, the following
+    // approach is adopted:
+    // 1. the ChatGPT run-steps request is initially mocked to return an empty data field, and
+    // 2. the sleep function is mocked to replace the empty response with a valid one, instead of
+    // pausing execution
+    mockRetrieveRunSteps("chatGptRunStepsEmptyResponse.json");
 
-        try (MockedStatic<ThreadUtils> mocked = Mockito.mockStatic(ThreadUtils.class)) {
-            mocked.when(() -> ThreadUtils.threadSleep(Mockito.anyLong())).thenAnswer(invocation -> {
+    try (MockedStatic<ThreadUtils> mocked = Mockito.mockStatic(ThreadUtils.class)) {
+      mocked
+          .when(() -> ThreadUtils.threadSleep(Mockito.anyLong()))
+          .thenAnswer(
+              invocation -> {
                 mockRetrieveRunSteps("chatGptRunStepsResponse.json");
                 return null;
-            });
+              });
 
-            String reviewMessageCode = getReviewMessage("__files/chatGptRunStepsResponse.json", 0);
-            String reviewMessageCommitMessage = getReviewMessage("__files/chatGptRunStepsResponse.json", 1);
+      String reviewMessageCode = getReviewMessage("__files/chatGptRunStepsResponse.json", 0);
+      String reviewMessageCommitMessage =
+          getReviewMessage("__files/chatGptRunStepsResponse.json", 1);
 
-            String reviewPrompt = chatGptPromptStateful.getDefaultGptThreadReviewMessage(formattedPatchContent);
+      String reviewPrompt =
+          chatGptPromptStateful.getDefaultGptThreadReviewMessage(formattedPatchContent);
 
-            handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
+      handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
-            ArgumentCaptor<ReviewInput> captor = testRequestSent();
-            Assert.assertEquals(reviewPrompt, requestContent);
-            Assert.assertEquals(reviewMessageCode, getCapturedMessage(captor, "test_file_1.py"));
-            Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
-        }
+      ArgumentCaptor<ReviewInput> captor = testRequestSent();
+      Assert.assertEquals(reviewPrompt, requestContent);
+      Assert.assertEquals(reviewMessageCode, getCapturedMessage(captor, "test_file_1.py"));
+      Assert.assertEquals(
+          reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
     }
+  }
 
-    @Test
-    public void gptMentionedInComment() throws RestApiException {
-        String reviewMessageCommitMessage = getReviewMessage("__files/chatGptResponseRequestStateful.json", 0);
+  @Test
+  public void gptMentionedInComment() throws RestApiException {
+    String reviewMessageCommitMessage =
+        getReviewMessage("__files/chatGptResponseRequestStateful.json", 0);
 
-        chatGptPromptStateful.setCommentEvent(true);
-        mockRetrieveRunSteps("chatGptResponseRequestStateful.json");
+    chatGptPromptStateful.setCommentEvent(true);
+    mockRetrieveRunSteps("chatGptResponseRequestStateful.json");
 
-        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
+    handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
 
-        ArgumentCaptor<ReviewInput> captor = testRequestSent();
-        Assert.assertEquals(promptTagComments, requestContent);
-        Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
-    }
+    ArgumentCaptor<ReviewInput> captor = testRequestSent();
+    Assert.assertEquals(promptTagComments, requestContent);
+    Assert.assertEquals(
+        reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
+  }
 
-    @Test
-    public void gptMentionedInCommentMessageResponseText() throws RestApiException {
-        String reviewMessageCommitMessage = getReviewMessage("__files/chatGptResponseRequestStateful.json", 0);
+  @Test
+  public void gptMentionedInCommentMessageResponseText() throws RestApiException {
+    String reviewMessageCommitMessage =
+        getReviewMessage("__files/chatGptResponseRequestStateful.json", 0);
 
-        chatGptPromptStateful.setCommentEvent(true);
-        mockRetrieveRunSteps("chatGptResponseRequestMessageStateful.json");
-        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.threadMessageRetrieveUri(AI_CHAT_THREAD_ID, AI_CHAT_MESSAGE_ID)).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBodyFile("chatGptResponseThreadMessageText.json")));
+    chatGptPromptStateful.setCommentEvent(true);
+    mockRetrieveRunSteps("chatGptResponseRequestMessageStateful.json");
+    WireMock.stubFor(
+        WireMock.get(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.threadMessageRetrieveUri(
+                                    AI_CHAT_THREAD_ID, AI_CHAT_MESSAGE_ID))
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBodyFile("chatGptResponseThreadMessageText.json")));
 
-        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
+    handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
 
-        ArgumentCaptor<ReviewInput> captor = testRequestSent();
-        Assert.assertEquals(promptTagComments, requestContent);
-        Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
-    }
+    ArgumentCaptor<ReviewInput> captor = testRequestSent();
+    Assert.assertEquals(promptTagComments, requestContent);
+    Assert.assertEquals(
+        reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
+  }
 
-    @Test
-    public void gptMentionedInCommentMessageResponseJson() throws RestApiException {
-        String reviewMessageCommitMessage = getReviewMessage("__files/chatGptResponseRequestStateful.json", 0);
+  @Test
+  public void gptMentionedInCommentMessageResponseJson() throws RestApiException {
+    String reviewMessageCommitMessage =
+        getReviewMessage("__files/chatGptResponseRequestStateful.json", 0);
 
-        chatGptPromptStateful.setCommentEvent(true);
-        mockRetrieveRunSteps("chatGptResponseRequestMessageStateful.json");
-        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateful.threadMessageRetrieveUri(AI_CHAT_THREAD_ID, AI_CHAT_MESSAGE_ID)).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBodyFile("chatGptResponseThreadMessageJson.json")));
+    chatGptPromptStateful.setCommentEvent(true);
+    mockRetrieveRunSteps("chatGptResponseRequestMessageStateful.json");
+    WireMock.stubFor(
+        WireMock.get(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain()
+                                + UriResourceLocatorStateful.threadMessageRetrieveUri(
+                                    AI_CHAT_THREAD_ID, AI_CHAT_MESSAGE_ID))
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBodyFile("chatGptResponseThreadMessageJson.json")));
 
-        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
+    handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
 
-        ArgumentCaptor<ReviewInput> captor = testRequestSent();
-        Assert.assertEquals(promptTagComments, requestContent);
-        Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
-    }
+    ArgumentCaptor<ReviewInput> captor = testRequestSent();
+    Assert.assertEquals(promptTagComments, requestContent);
+    Assert.assertEquals(
+        reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
+  }
 
-    @Test
-    public void gerritMergedCommits() {
-        projectHandler.removeValue(KEY_VECTOR_STORE_ID);
-        handleEventBasedOnType(SupportedEvents.CHANGE_MERGED);
+  @Test
+  public void gerritMergedCommits() {
+    projectHandler.removeValue(KEY_VECTOR_STORE_ID);
+    handleEventBasedOnType(SupportedEvents.CHANGE_MERGED);
 
-        Assert.assertEquals(AI_CHAT_VECTOR_ID, projectHandler.getValue(KEY_VECTOR_STORE_ID));
-    }
+    Assert.assertEquals(AI_CHAT_VECTOR_ID, projectHandler.getValue(KEY_VECTOR_STORE_ID));
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatelessTest.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatelessTest.java
index d25c9c9..6202b2e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatelessTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewStatelessTest.java
@@ -1,9 +1,29 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
+import static com.googlesource.gerrit.plugins.aicodereview.config.Configuration.KEY_AI_CHAT_ENDPOINT;
+import static com.googlesource.gerrit.plugins.aicodereview.config.Configuration.KEY_AI_TYPE;
+import static com.googlesource.gerrit.plugins.aicodereview.config.Configuration.KEY_STREAM_OUTPUT;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import com.github.tomakehurst.wiremock.client.WireMock;
 import com.google.common.net.HttpHeaders;
-import com.google.gson.JsonArray;
-import com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask;
 import com.google.gerrit.extensions.api.changes.FileApi;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.common.DiffInfo;
@@ -11,11 +31,16 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.json.OutputFormat;
 import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask;
+import com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.SupportedEvents;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.UriResourceLocatorStateless;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.prompt.AIChatPromptStateless;
 import com.googlesource.gerrit.plugins.aicodereview.settings.Settings;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Map;
 import lombok.extern.slf4j.Slf4j;
-
 import org.apache.commons.lang3.reflect.TypeLiteral;
 import org.apache.http.entity.ContentType;
 import org.junit.Assert;
@@ -25,235 +50,250 @@
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.net.URI;
-import java.util.Arrays;
-import java.util.Map;
-
-import static com.googlesource.gerrit.plugins.aicodereview.config.Configuration.*;
-import static com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.SupportedEvents;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.TextUtils.joinWithNewLine;
-import static java.net.HttpURLConnection.HTTP_OK;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 @Slf4j
 @RunWith(MockitoJUnitRunner.class)
 public class AIChatReviewStatelessTest extends AIChatReviewTestBase {
-    private ReviewInput expectedResponseStreamed;
-    private String expectedSystemPromptReview;
-    private String promptTagReview;
-    private String diffContent;
-    private ReviewInput gerritPatchSetReview;
-    private JsonArray prompts;
+  private ReviewInput expectedResponseStreamed;
+  private String expectedSystemPromptReview;
+  private String promptTagReview;
+  private String diffContent;
+  private ReviewInput gerritPatchSetReview;
+  private JsonArray prompts;
 
-    private AIChatPromptStateless AIChatPromptStateless;
+  private AIChatPromptStateless AIChatPromptStateless;
 
-    protected void initConfig() {
-        super.initGlobalAndProjectConfig();
+  protected void initConfig() {
+    super.initGlobalAndProjectConfig();
 
-        when(globalConfig.getBoolean(Mockito.eq(KEY_STREAM_OUTPUT), Mockito.anyBoolean()))
-                .thenReturn(GPT_STREAM_OUTPUT);
-        when(globalConfig.getBoolean(Mockito.eq("aiReviewCommitMessages"), Mockito.anyBoolean()))
-                .thenReturn(true);
+    when(globalConfig.getBoolean(Mockito.eq(KEY_STREAM_OUTPUT), Mockito.anyBoolean()))
+        .thenReturn(GPT_STREAM_OUTPUT);
+    when(globalConfig.getBoolean(Mockito.eq("aiReviewCommitMessages"), Mockito.anyBoolean()))
+        .thenReturn(true);
 
-        super.initConfig();
+    super.initConfig();
 
-        // Load the prompts
-        AIChatPromptStateless = new AIChatPromptStateless(config);
-    }
+    // Load the prompts
+    AIChatPromptStateless = new AIChatPromptStateless(config);
+  }
 
-    protected void setupMockRequests() throws RestApiException {
-        super.setupMockRequests();
+  protected void setupMockRequests() throws RestApiException {
+    super.setupMockRequests();
 
-        // Mock the behavior of the gerritPatchSetFiles request
-        Map<String, FileInfo> files = readTestFileToType(
-                "__files/stateless/gerritPatchSetFiles.json",
-                new TypeLiteral<Map<String, FileInfo>>() {}.getType()
-        );
-        when(revisionApiMock.files(0)).thenReturn(files);
+    // Mock the behavior of the gerritPatchSetFiles request
+    Map<String, FileInfo> files =
+        readTestFileToType(
+            "__files/stateless/gerritPatchSetFiles.json",
+            new TypeLiteral<Map<String, FileInfo>>() {}.getType());
+    when(revisionApiMock.files(0)).thenReturn(files);
 
-        // Mock the behavior of the gerritPatchSet diff requests
-        FileApi commitMsgFileMock = mock(FileApi.class);
-        when(revisionApiMock.file("/COMMIT_MSG")).thenReturn(commitMsgFileMock);
-        DiffInfo commitMsgFileDiff = readTestFileToClass("__files/stateless/gerritPatchSetDiffCommitMsg.json", DiffInfo.class);
-        when(commitMsgFileMock.diff(0)).thenReturn(commitMsgFileDiff);
-        FileApi testFileMock = mock(FileApi.class);
-        when(revisionApiMock.file("test_file.py")).thenReturn(testFileMock);
-        DiffInfo testFileDiff = readTestFileToClass("__files/stateless/gerritPatchSetDiffTestFile.json", DiffInfo.class);
-        when(testFileMock.diff(0)).thenReturn(testFileDiff);
+    // Mock the behavior of the gerritPatchSet diff requests
+    FileApi commitMsgFileMock = mock(FileApi.class);
+    when(revisionApiMock.file("/COMMIT_MSG")).thenReturn(commitMsgFileMock);
+    DiffInfo commitMsgFileDiff =
+        readTestFileToClass("__files/stateless/gerritPatchSetDiffCommitMsg.json", DiffInfo.class);
+    when(commitMsgFileMock.diff(0)).thenReturn(commitMsgFileDiff);
+    FileApi testFileMock = mock(FileApi.class);
+    when(revisionApiMock.file("test_file.py")).thenReturn(testFileMock);
+    DiffInfo testFileDiff =
+        readTestFileToClass("__files/stateless/gerritPatchSetDiffTestFile.json", DiffInfo.class);
+    when(testFileMock.diff(0)).thenReturn(testFileDiff);
 
-        // Mock the behavior of the askGpt request
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateless.chatCompletionsUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBodyFile("aiChatResponseStreamed.txt")));
-    }
+    // Mock the behavior of the askGpt request
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain() + UriResourceLocatorStateless.chatCompletionsUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBodyFile("aiChatResponseStreamed.txt")));
+  }
 
-    protected void initComparisonContent() {
-        super.initComparisonContent();
+  protected void initComparisonContent() {
+    super.initComparisonContent();
 
-        diffContent = readTestFile("reducePatchSet/patchSetDiffOutput.json");
-        gerritPatchSetReview = readTestFileToClass("__files/stateless/gerritPatchSetReview.json", ReviewInput.class);
-        expectedResponseStreamed = readTestFileToClass("__files/stateless/aiChatExpectedResponseStreamed.json", ReviewInput.class);
-        promptTagReview = readTestFile("__files/stateless/aiChatPromptTagReview.json");
-        promptTagComments = readTestFile("__files/stateless/aiChatPromptTagRequests.json");
-        expectedSystemPromptReview = AIChatPromptStateless.getDefaultGptReviewSystemPrompt();
-    }
+    diffContent = readTestFile("reducePatchSet/patchSetDiffOutput.json");
+    gerritPatchSetReview =
+        readTestFileToClass("__files/stateless/gerritPatchSetReview.json", ReviewInput.class);
+    expectedResponseStreamed =
+        readTestFileToClass(
+            "__files/stateless/aiChatExpectedResponseStreamed.json", ReviewInput.class);
+    promptTagReview = readTestFile("__files/stateless/aiChatPromptTagReview.json");
+    promptTagComments = readTestFile("__files/stateless/aiChatPromptTagRequests.json");
+    expectedSystemPromptReview = AIChatPromptStateless.getDefaultGptReviewSystemPrompt();
+  }
 
-    protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
-        ArgumentCaptor<ReviewInput> reviewInputCaptor = super.testRequestSent();
-        prompts = gptRequestBody.get("messages").getAsJsonArray();
-        return reviewInputCaptor;
-    }
+  protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
+    ArgumentCaptor<ReviewInput> reviewInputCaptor = super.testRequestSent();
+    prompts = gptRequestBody.get("messages").getAsJsonArray();
+    return reviewInputCaptor;
+  }
 
-    private String getReviewUserPrompt() {
-        return joinWithNewLine(Arrays.asList(
-                AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT,
-                AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_REVIEW + " " +
-                        AIChatPromptStateless.DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT + " " +
-                        AIChatPromptStateless.getPatchSetReviewPrompt(),
-                AIChatPromptStateless.getReviewPromptCommitMessages(),
-                AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_DIFF,
-                diffContent,
-                AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_MESSAGE_HISTORY,
-                promptTagReview
-        ));
-    }
+  private String getReviewUserPrompt() {
+    return joinWithNewLine(
+        Arrays.asList(
+            AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT,
+            AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_REVIEW
+                + " "
+                + AIChatPromptStateless.DEFAULT_AI_CHAT_PROMPT_FORCE_JSON_FORMAT
+                + " "
+                + AIChatPromptStateless.getPatchSetReviewPrompt(),
+            AIChatPromptStateless.getReviewPromptCommitMessages(),
+            AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_DIFF,
+            diffContent,
+            AIChatPromptStateless.DEFAULT_AI_CHAT_REVIEW_PROMPT_MESSAGE_HISTORY,
+            promptTagReview));
+  }
 
-    @Test
-    public void patchSetCreatedOrUpdatedStreamed() throws Exception {
-        String reviewUserPrompt = getReviewUserPrompt();
-        AIChatPromptStateless.setCommentEvent(false);
+  @Test
+  public void patchSetCreatedOrUpdatedStreamed() throws Exception {
+    String reviewUserPrompt = getReviewUserPrompt();
+    AIChatPromptStateless.setCommentEvent(false);
 
-        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
+    handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
-        ArgumentCaptor<ReviewInput> captor = testRequestSent();
-        String systemPrompt = prompts.get(0).getAsJsonObject().get("content").getAsString();
-        Assert.assertEquals(expectedSystemPromptReview, systemPrompt);
-        String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
-        Assert.assertEquals(reviewUserPrompt, userPrompt);
+    ArgumentCaptor<ReviewInput> captor = testRequestSent();
+    String systemPrompt = prompts.get(0).getAsJsonObject().get("content").getAsString();
+    Assert.assertEquals(expectedSystemPromptReview, systemPrompt);
+    String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
+    Assert.assertEquals(reviewUserPrompt, userPrompt);
 
-        Gson gson = OutputFormat.JSON_COMPACT.newGson();
-        Assert.assertEquals(gson.toJson(expectedResponseStreamed), gson.toJson(captor.getAllValues().get(0)));
-    }
+    Gson gson = OutputFormat.JSON_COMPACT.newGson();
+    Assert.assertEquals(
+        gson.toJson(expectedResponseStreamed), gson.toJson(captor.getAllValues().get(0)));
+  }
 
-    @Test
-    public void patchSetCreatedOrUpdatedUnstreamed() throws Exception {
-        when(globalConfig.getBoolean(Mockito.eq("aiStreamOutput"), Mockito.anyBoolean()))
-                .thenReturn(false);
-        when(globalConfig.getBoolean(Mockito.eq("enabledVoting"), Mockito.anyBoolean()))
-                .thenReturn(true);
+  @Test
+  public void patchSetCreatedOrUpdatedUnstreamed() throws Exception {
+    when(globalConfig.getBoolean(Mockito.eq("aiStreamOutput"), Mockito.anyBoolean()))
+        .thenReturn(false);
+    when(globalConfig.getBoolean(Mockito.eq("enabledVoting"), Mockito.anyBoolean()))
+        .thenReturn(true);
 
-        String reviewUserPrompt = getReviewUserPrompt();
-        AIChatPromptStateless.setCommentEvent(false);
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateless.chatCompletionsUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBodyFile("aiChatResponseReview.json")));
+    String reviewUserPrompt = getReviewUserPrompt();
+    AIChatPromptStateless.setCommentEvent(false);
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain() + UriResourceLocatorStateless.chatCompletionsUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBodyFile("aiChatResponseReview.json")));
 
-        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
+    handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
-        ArgumentCaptor<ReviewInput> captor = testRequestSent();
-        String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
-        Assert.assertEquals(reviewUserPrompt, userPrompt);
+    ArgumentCaptor<ReviewInput> captor = testRequestSent();
+    String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
+    Assert.assertEquals(reviewUserPrompt, userPrompt);
 
-        Gson gson = OutputFormat.JSON_COMPACT.newGson();
-        Assert.assertEquals(gson.toJson(gerritPatchSetReview), gson.toJson(captor.getAllValues().get(0)));
-    }
+    Gson gson = OutputFormat.JSON_COMPACT.newGson();
+    Assert.assertEquals(
+        gson.toJson(gerritPatchSetReview), gson.toJson(captor.getAllValues().get(0)));
+  }
 
-    @Test
-    public void patchSetDisableUserGroup() {
-        when(globalConfig.getString(Mockito.eq("disabledGroups"), Mockito.anyString()))
-                .thenReturn(GERRIT_USER_GROUP);
+  @Test
+  public void patchSetDisableUserGroup() {
+    when(globalConfig.getString(Mockito.eq("disabledGroups"), Mockito.anyString()))
+        .thenReturn(GERRIT_USER_GROUP);
 
-        Assert.assertEquals(EventHandlerTask.Result.NOT_SUPPORTED, handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED));
-    }
+    Assert.assertEquals(
+        EventHandlerTask.Result.NOT_SUPPORTED,
+        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED));
+  }
 
-    @Test
-    public void gptMentionedInComment() throws RestApiException {
-        when(config.getGerritUserName()).thenReturn(GERRIT_GPT_USERNAME);
-        AIChatPromptStateless.setCommentEvent(true);
-        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo(URI.create(config.getAIDomain()
-                        + UriResourceLocatorStateless.chatCompletionsUri()).getPath()))
-                .willReturn(WireMock.aResponse()
-                        .withStatus(HTTP_OK)
-                        .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
-                        .withBodyFile("aiChatResponseRequestStateless.json")));
+  @Test
+  public void gptMentionedInComment() throws RestApiException {
+    when(config.getGerritUserName()).thenReturn(GERRIT_GPT_USERNAME);
+    AIChatPromptStateless.setCommentEvent(true);
+    WireMock.stubFor(
+        WireMock.post(
+                WireMock.urlEqualTo(
+                    URI.create(
+                            config.getAIDomain() + UriResourceLocatorStateless.chatCompletionsUri())
+                        .getPath()))
+            .willReturn(
+                WireMock.aResponse()
+                    .withStatus(HTTP_OK)
+                    .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
+                    .withBodyFile("aiChatResponseRequestStateless.json")));
 
-        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
-        int commentPropertiesSize = gerritClient.getClientData(getGerritChange()).getCommentProperties().size();
+    handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
+    int commentPropertiesSize =
+        gerritClient.getClientData(getGerritChange()).getCommentProperties().size();
 
-        String commentUserPrompt = joinWithNewLine(Arrays.asList(
+    String commentUserPrompt =
+        joinWithNewLine(
+            Arrays.asList(
                 AIChatPromptStateless.DEFAULT_AI_CHAT_REQUEST_PROMPT_DIFF,
                 diffContent,
                 AIChatPromptStateless.DEFAULT_AI_CHAT_REQUEST_PROMPT_REQUESTS,
                 readTestFile("__files/stateless/aiChatExpectedRequestMessage.json"),
-                AIChatPromptStateless.getCommentRequestPrompt(commentPropertiesSize)
-        ));
-        testRequestSent();
-        String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
-        Assert.assertEquals(commentUserPrompt, userPrompt);
-    }
+                AIChatPromptStateless.getCommentRequestPrompt(commentPropertiesSize)));
+    testRequestSent();
+    String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
+    Assert.assertEquals(commentUserPrompt, userPrompt);
+  }
 
-    @Test
-    public void testAITypeValidOptions(){
-        when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString()))
-                .thenReturn("CHATGPT");
+  @Test
+  public void testAITypeValidOptions() {
+    when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString())).thenReturn("CHATGPT");
 
-        // check default for aiType is chatGPT.
-        Assert.assertEquals(config.getAIType(), Settings.AIType.CHATGPT);
+    // check default for aiType is chatGPT.
+    Assert.assertEquals(config.getAIType(), Settings.AIType.CHATGPT);
 
-        when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString()))
-                .thenReturn("OLLAMA");
+    when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString())).thenReturn("OLLAMA");
 
-        Assert.assertEquals(config.getAIType(), Settings.AIType.OLLAMA);
-    }
+    Assert.assertEquals(config.getAIType(), Settings.AIType.OLLAMA);
+  }
 
-    @Test
-    public void testAITypeControlsEndpoint(){
-        when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString()))
-                .thenReturn("CHATGPT");
+  @Test
+  public void testAITypeControlsEndpoint() {
+    when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString())).thenReturn("CHATGPT");
 
-        // check default for aiType is chatGPT.
-        Assert.assertEquals(config.getChatEndpoint(), "");
-        Assert.assertEquals(UriResourceLocatorStateless.chatCompletionsUri(),
-                UriResourceLocatorStateless.getChatResourceUri(config));
+    // check default for aiType is chatGPT.
+    Assert.assertEquals(config.getChatEndpoint(), "");
+    Assert.assertEquals(
+        UriResourceLocatorStateless.chatCompletionsUri(),
+        UriResourceLocatorStateless.getChatResourceUri(config));
 
-        // swap it to ollama, check we still get the chatCompletionsUri, as its the openai
-        // compat endpoint we use.
-        when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString()))
-                .thenReturn("OLLAMA");
-        Assert.assertEquals(UriResourceLocatorStateless.chatCompletionsUri(),
-                UriResourceLocatorStateless.getChatResourceUri(config));
+    // swap it to ollama, check we still get the chatCompletionsUri, as its the openai
+    // compat endpoint we use.
+    when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString())).thenReturn("OLLAMA");
+    Assert.assertEquals(
+        UriResourceLocatorStateless.chatCompletionsUri(),
+        UriResourceLocatorStateless.getChatResourceUri(config));
 
-        // finally change to GENERIC, and check that we can specify any endpoint
-        when(globalConfig.getString(Mockito.eq(KEY_AI_TYPE), Mockito.anyString()))
-                .thenReturn("GENERIC");
+    // finally change to GENERIC, and check that we can specify any endpoint
+    when(globalConfig.getString(Mockito.eq(KEY_AI_TYPE), Mockito.anyString()))
+        .thenReturn("GENERIC");
 
-        final String expectedValueForEndpoint = "/someendpoint/someapi/chat";
-        when(globalConfig.getString(Mockito.eq(KEY_AI_CHAT_ENDPOINT), Mockito.anyString()))
-                .thenReturn(expectedValueForEndpoint);
-        Assert.assertEquals(expectedValueForEndpoint,
-                UriResourceLocatorStateless.getChatResourceUri(config));
-    }
+    final String expectedValueForEndpoint = "/someendpoint/someapi/chat";
+    when(globalConfig.getString(Mockito.eq(KEY_AI_CHAT_ENDPOINT), Mockito.anyString()))
+        .thenReturn(expectedValueForEndpoint);
+    Assert.assertEquals(
+        expectedValueForEndpoint, UriResourceLocatorStateless.getChatResourceUri(config));
+  }
 
-    @Test
-    public void testAITypeControlsAuthHeader(){
-        when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString()))
-                .thenReturn("CHATGPT");
+  @Test
+  public void testAITypeControlsAuthHeader() {
+    when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString())).thenReturn("CHATGPT");
 
-        // check default for aiType is chatGPT.
-        Assert.assertEquals("Authorization", config.getAuthorizationHeaderInfo().getName());
-        Assert.assertEquals("Bearer " + config.getAIToken(), config.getAuthorizationHeaderInfo().getValue());
+    // check default for aiType is chatGPT.
+    Assert.assertEquals("Authorization", config.getAuthorizationHeaderInfo().getName());
+    Assert.assertEquals(
+        "Bearer " + config.getAIToken(), config.getAuthorizationHeaderInfo().getValue());
 
-        // swap it to ollama, check we still get the chatCompletionsUri, as its the openai
-        // compat endpoint we use.
-        when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString()))
-                .thenReturn("OLLAMA");
-        Assert.assertNull("No expected value for auth header for ollama",  config.getAuthorizationHeaderInfo());
-    }
+    // swap it to ollama, check we still get the chatCompletionsUri, as its the openai
+    // compat endpoint we use.
+    when(globalConfig.getString(Mockito.eq("aiType"), Mockito.anyString())).thenReturn("OLLAMA");
+    Assert.assertNull(
+        "No expected value for auth header for ollama", config.getAuthorizationHeaderInfo());
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewTestBase.java
index 1d81253..469f218 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatReviewTestBase.java
@@ -1,9 +1,29 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
+import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
+import static com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.EVENT_CLASS_MAP;
+import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import com.google.gerrit.entities.Account;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.api.accounts.AccountApi;
 import com.google.gerrit.extensions.api.accounts.Accounts;
@@ -14,6 +34,8 @@
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.json.OutputFormat;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.data.AccountAttribute;
 import com.google.gerrit.server.data.PatchSetAttribute;
@@ -29,8 +51,8 @@
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
-import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.openapi.ChatAIClient;
 import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.gerrit.GerritClientPatchSet;
+import com.googlesource.gerrit.plugins.aicodereview.interfaces.mode.common.client.api.openapi.ChatAIClient;
 import com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask;
 import com.googlesource.gerrit.plugins.aicodereview.localization.Localizer;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritClient;
@@ -43,14 +65,6 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateful.client.api.git.GitRepoFiles;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.chatai.AIChatClientStateless;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.api.gerrit.GerritClientPatchSetStateless;
-import lombok.NonNull;
-import org.junit.Before;
-import org.junit.Rule;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.nio.file.Files;
@@ -59,345 +73,336 @@
 import java.time.Instant;
 import java.util.Collections;
 import java.util.List;
-import java.util.Optional;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Consumer;
-
-import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
-import static com.googlesource.gerrit.plugins.aicodereview.listener.EventHandlerTask.EVENT_CLASS_MAP;
-import static com.googlesource.gerrit.plugins.aicodereview.utils.GsonUtils.getGson;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import lombok.NonNull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
 
 public class AIChatReviewTestBase extends AIChatTestBase {
-    protected static final Path basePath = Paths.get("src/test/resources");
-    public static final int GERRIT_GPT_ACCOUNT_ID = 1000000;
-    public static final String GERRIT_GPT_USERNAME = "gpt";
-    public static final int GERRIT_USER_ACCOUNT_ID = 1000001;
-    protected static final String GERRIT_USER_ACCOUNT_NAME = "Test";
-    protected static final String GERRIT_USER_ACCOUNT_EMAIL = "test@example.com";
-    protected static final String GERRIT_USER_USERNAME = "test";
-    protected static final String GERRIT_USER_GROUP = "Test";
-    protected static final String AI_TOKEN = "tk-test";
-    protected static final String GPT_DOMAIN = "http://localhost:9527";
-    protected static final boolean GPT_STREAM_OUTPUT = true;
-    protected static final long TEST_TIMESTAMP = 1699270812;
-    private static  final int GPT_USER_ACCOUNT_ID = 1000000;
+  protected static final Path basePath = Paths.get("src/test/resources");
+  public static final int GERRIT_GPT_ACCOUNT_ID = 1000000;
+  public static final String GERRIT_GPT_USERNAME = "gpt";
+  public static final int GERRIT_USER_ACCOUNT_ID = 1000001;
+  protected static final String GERRIT_USER_ACCOUNT_NAME = "Test";
+  protected static final String GERRIT_USER_ACCOUNT_EMAIL = "test@example.com";
+  protected static final String GERRIT_USER_USERNAME = "test";
+  protected static final String GERRIT_USER_GROUP = "Test";
+  protected static final String AI_TOKEN = "tk-test";
+  protected static final String GPT_DOMAIN = "http://localhost:9527";
+  protected static final boolean GPT_STREAM_OUTPUT = true;
+  protected static final long TEST_TIMESTAMP = 1699270812;
+  private static final int GPT_USER_ACCOUNT_ID = 1000000;
 
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(9527);
+  @Rule public WireMockRule wireMockRule = new WireMockRule(9527);
 
-    @Mock
-    protected GitRepoFiles gitRepoFiles;
+  @Mock protected GitRepoFiles gitRepoFiles;
 
-    @Mock
-    protected PluginDataHandlerProvider pluginDataHandlerProvider;
+  @Mock protected PluginDataHandlerProvider pluginDataHandlerProvider;
 
-    @Mock
-    protected PluginDataHandler pluginDataHandler;
+  @Mock protected PluginDataHandler pluginDataHandler;
 
-    @Mock
-    protected OneOffRequestContext context;
-    @Mock
-    protected GerritApi gerritApi;
-    @Mock
-    protected Changes changesMock;
-    @Mock
-    protected ChangeApi changeApiMock;
-    @Mock
-    protected RevisionApi revisionApiMock;
-    @Mock
-    protected ReviewResult reviewResult;
-    @Mock
-    protected CommentsRequest commentsRequestMock;
-    @Mock
-    protected AccountCache accountCacheMock;
+  @Mock protected OneOffRequestContext context;
+  @Mock protected GerritApi gerritApi;
+  @Mock protected Changes changesMock;
+  @Mock protected ChangeApi changeApiMock;
+  @Mock protected RevisionApi revisionApiMock;
+  @Mock protected ReviewResult reviewResult;
+  @Mock protected CommentsRequest commentsRequestMock;
+  @Mock protected AccountCache accountCacheMock;
 
-    protected PluginConfig globalConfig;
-    protected PluginConfig projectConfig;
-    protected Configuration config;
-    protected ChangeSetData changeSetData;
-    protected GerritClient gerritClient;
-    protected PatchSetReviewer patchSetReviewer;
-    protected ConfigCreator mockConfigCreator;
-    protected JsonObject gptRequestBody;
-    protected String promptTagComments;
+  protected PluginConfig globalConfig;
+  protected PluginConfig projectConfig;
+  protected Configuration config;
+  protected ChangeSetData changeSetData;
+  protected GerritClient gerritClient;
+  protected PatchSetReviewer patchSetReviewer;
+  protected ConfigCreator mockConfigCreator;
+  protected JsonObject gptRequestBody;
+  protected String promptTagComments;
 
-    @Before
-    public void before() throws RestApiException {
-        initGlobalAndProjectConfig();
-        initConfig();
-        setupMockRequests();
-        initComparisonContent();
-        initTest();
-    }
+  @Before
+  public void before() throws RestApiException {
+    initGlobalAndProjectConfig();
+    initConfig();
+    setupMockRequests();
+    initComparisonContent();
+    initTest();
+  }
 
-    protected void initGlobalAndProjectConfig() {
-        globalConfig = mock(PluginConfig.class);
-        Answer<Object> returnDefaultArgument = invocation -> {
-            // Return the second argument (i.e., the Default value) passed to the method
-            return invocation.getArgument(1);
+  protected void initGlobalAndProjectConfig() {
+    globalConfig = mock(PluginConfig.class);
+    Answer<Object> returnDefaultArgument =
+        invocation -> {
+          // Return the second argument (i.e., the Default value) passed to the method
+          return invocation.getArgument(1);
         };
 
-        // Mock the Global Config values not provided by Default
-        when(globalConfig.getString("aiToken")).thenReturn(AI_TOKEN);
+    // Mock the Global Config values not provided by Default
+    when(globalConfig.getString("aiToken")).thenReturn(AI_TOKEN);
 
-        // Mock the Global Config values to the Defaults passed as second arguments of the `get*` methods.
-        when(globalConfig.getString(Mockito.anyString(), Mockito.anyString())).thenAnswer(returnDefaultArgument);
-        when(globalConfig.getInt(Mockito.anyString(), Mockito.anyInt())).thenAnswer(returnDefaultArgument);
-        when(globalConfig.getBoolean(Mockito.anyString(), Mockito.anyBoolean())).thenAnswer(returnDefaultArgument);
+    // Mock the Global Config values to the Defaults passed as second arguments of the `get*`
+    // methods.
+    when(globalConfig.getString(Mockito.anyString(), Mockito.anyString()))
+        .thenAnswer(returnDefaultArgument);
+    when(globalConfig.getInt(Mockito.anyString(), Mockito.anyInt()))
+        .thenAnswer(returnDefaultArgument);
+    when(globalConfig.getBoolean(Mockito.anyString(), Mockito.anyBoolean()))
+        .thenAnswer(returnDefaultArgument);
 
-        // Mock the Global Config values that differ from the ones provided by Default
-        when(globalConfig.getString(Mockito.eq("aiDomain"), Mockito.anyString()))
-                .thenReturn(GPT_DOMAIN);
-        when(globalConfig.getString("gerritUserName")).thenReturn(GERRIT_GPT_USERNAME);
+    // Mock the Global Config values that differ from the ones provided by Default
+    when(globalConfig.getString(Mockito.eq("aiDomain"), Mockito.anyString()))
+        .thenReturn(GPT_DOMAIN);
+    when(globalConfig.getString("gerritUserName")).thenReturn(GERRIT_GPT_USERNAME);
 
-        projectConfig = mock(PluginConfig.class);
+    projectConfig = mock(PluginConfig.class);
 
-        // Mock the Project Config values
-        when(projectConfig.getBoolean(Mockito.eq("isEnabled"), Mockito.anyBoolean())).thenReturn(true);
+    // Mock the Project Config values
+    when(projectConfig.getBoolean(Mockito.eq("isEnabled"), Mockito.anyBoolean())).thenReturn(true);
+  }
+
+  protected void initConfig() {
+    config =
+        new Configuration(
+            context, gerritApi, globalConfig, projectConfig, "gpt@email.com", Account.id(1000000));
+  }
+
+  protected void setupMockRequests() throws RestApiException {
+    Accounts accountsMock = mockGerritAccountsRestEndpoint();
+    // Mock the behavior of the gerritAccountIdUri request
+    mockGerritAccountsQueryApiCall(GERRIT_GPT_USERNAME, GERRIT_GPT_ACCOUNT_ID);
+
+    // Mock the behavior of the gerritAccountIdUri request
+    mockGerritAccountsQueryApiCall(GERRIT_USER_USERNAME, GERRIT_USER_ACCOUNT_ID);
+
+    // Mock the behavior of the gerritAccountGroups request
+    mockGerritAccountGroupsApiCall(accountsMock, GERRIT_USER_ACCOUNT_ID);
+
+    mockGerritChangeApiRestEndpoint();
+
+    // Mock the behavior of the gerritGetPatchSetDetailUri request
+    mockGerritChangeDetailsApiCall();
+
+    // Mock the behavior of the gerritPatchSet comments request
+    mockGerritChangeCommentsApiCall();
+
+    // Mock the behavior of the gerrit Review request
+    mockGerritReviewApiCall();
+
+    // Mock the GerritApi's revision API
+    when(changeApiMock.current()).thenReturn(revisionApiMock);
+
+    // Mock the pluginDataHandlerProvider to return the mocked Change pluginDataHandler
+    when(pluginDataHandlerProvider.getChangeScope()).thenReturn(pluginDataHandler);
+  }
+
+  private Accounts mockGerritAccountsRestEndpoint() {
+    Accounts accountsMock = mock(Accounts.class);
+    when(gerritApi.accounts()).thenReturn(accountsMock);
+    return accountsMock;
+  }
+
+  private void mockGerritAccountsQueryApiCall(String username, int expectedAccountId) {
+    AccountState accountStateMock = mock(AccountState.class);
+    Account accountMock = mock(Account.class);
+    when(accountStateMock.account()).thenReturn(accountMock);
+    when(accountMock.id()).thenReturn(Account.id(expectedAccountId));
+    when(accountCacheMock.getByUsername(username)).thenReturn(Optional.of(accountStateMock));
+  }
+
+  private void mockGerritAccountGroupsApiCall(Accounts accountsMock, int accountId)
+      throws RestApiException {
+    Gson gson = OutputFormat.JSON.newGson();
+    List<GroupInfo> groups =
+        gson.fromJson(
+            readTestFile("__files/gerritAccountGroups.json"),
+            new TypeLiteral<List<GroupInfo>>() {}.getType());
+    AccountApi accountApiMock = mock(AccountApi.class);
+    when(accountsMock.id(accountId)).thenReturn(accountApiMock);
+    when(accountApiMock.getGroups()).thenReturn(groups);
+  }
+
+  private void mockGerritChangeDetailsApiCall() throws RestApiException {
+    ChangeInfo changeInfo =
+        readTestFileToClass("__files/gerritPatchSetDetail.json", ChangeInfo.class);
+    when(changeApiMock.get()).thenReturn(changeInfo);
+  }
+
+  private void mockGerritChangeCommentsApiCall() throws RestApiException {
+    Map<String, List<CommentInfo>> comments =
+        readTestFileToType(
+            "__files/gerritPatchSetComments.json",
+            new TypeLiteral<Map<String, List<CommentInfo>>>() {}.getType());
+    when(changeApiMock.commentsRequest()).thenReturn(commentsRequestMock);
+    when(commentsRequestMock.get()).thenReturn(comments);
+  }
+
+  private void mockGerritChangeApiRestEndpoint() throws RestApiException {
+    when(gerritApi.changes()).thenReturn(changesMock);
+    when(changesMock.id(PROJECT_NAME.get(), BRANCH_NAME.shortName(), CHANGE_ID.get()))
+        .thenReturn(changeApiMock);
+  }
+
+  private void mockGerritReviewApiCall() throws RestApiException {
+    ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class);
+    when(revisionApiMock.review(reviewInputCaptor.capture())).thenReturn(reviewResult);
+  }
+
+  protected void initComparisonContent() {}
+
+  protected <T> T readTestFileToClass(String filename, Class<T> clazz) {
+    Gson gson = OutputFormat.JSON.newGson();
+    return gson.fromJson(readTestFile(filename), clazz);
+  }
+
+  protected <T> T readTestFileToType(String filename, Type type) {
+    Gson gson = OutputFormat.JSON.newGson();
+    return gson.fromJson(readTestFile(filename), type);
+  }
+
+  protected String readTestFile(String filename) {
+    try {
+      return new String(Files.readAllBytes(basePath.resolve(filename)));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
     }
+  }
 
-    protected void initConfig() {
-        config = new Configuration(
-                context,
-                gerritApi,
-                globalConfig,
-                projectConfig,
-                "gpt@email.com",
-                Account.id(1000000)
-        );
-    }
+  protected EventHandlerTask.Result handleEventBasedOnType(
+      EventHandlerTask.SupportedEvents triggeredEvent) {
+    Consumer<Event> typeSpecificSetup = getTypeSpecificSetup(triggeredEvent);
+    Event event = getMockedEvent(triggeredEvent);
+    setupCommonEventMocks((PatchSetEvent) event); // Apply common mock configurations
+    typeSpecificSetup.accept(event);
 
-    protected void setupMockRequests() throws RestApiException {
-        Accounts accountsMock = mockGerritAccountsRestEndpoint();
-        // Mock the behavior of the gerritAccountIdUri request
-        mockGerritAccountsQueryApiCall(GERRIT_GPT_USERNAME, GERRIT_GPT_ACCOUNT_ID);
+    EventHandlerTask task =
+        Guice.createInjector(
+                new AbstractModule() {
+                  @Override
+                  protected void configure() {
+                    install(new TestGerritEventContextModule(config, event));
 
-        // Mock the behavior of the gerritAccountIdUri request
-        mockGerritAccountsQueryApiCall(GERRIT_USER_USERNAME, GERRIT_USER_ACCOUNT_ID);
+                    bind(GerritClient.class).toInstance(gerritClient);
+                    bind(GitRepoFiles.class).toInstance(gitRepoFiles);
+                    bind(ConfigCreator.class).toInstance(mockConfigCreator);
+                    bind(PatchSetReviewer.class).toInstance(patchSetReviewer);
+                    bind(PluginDataHandlerProvider.class).toInstance(pluginDataHandlerProvider);
+                    bind(AccountCache.class).toInstance(mockAccountCache());
+                  }
+                })
+            .getInstance(EventHandlerTask.class);
+    return task.execute();
+  }
 
-        // Mock the behavior of the gerritAccountGroups request
-        mockGerritAccountGroupsApiCall(accountsMock, GERRIT_USER_ACCOUNT_ID);
+  protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
+    ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class);
+    verify(revisionApiMock).review(reviewInputCaptor.capture());
+    gptRequestBody =
+        getGson().fromJson(patchSetReviewer.getChatGptClient().getRequestBody(), JsonObject.class);
+    return reviewInputCaptor;
+  }
 
-        mockGerritChangeApiRestEndpoint();
-
-        // Mock the behavior of the gerritGetPatchSetDetailUri request
-        mockGerritChangeDetailsApiCall();
-
-        // Mock the behavior of the gerritPatchSet comments request
-        mockGerritChangeCommentsApiCall();
-
-        // Mock the behavior of the gerrit Review request
-        mockGerritReviewApiCall();
-
-        // Mock the GerritApi's revision API
-        when(changeApiMock.current()).thenReturn(revisionApiMock);
-
-        // Mock the pluginDataHandlerProvider to return the mocked Change pluginDataHandler
-        when(pluginDataHandlerProvider.getChangeScope()).thenReturn(pluginDataHandler);
-    }
-
-    private Accounts mockGerritAccountsRestEndpoint() {
-        Accounts accountsMock = mock(Accounts.class);
-        when(gerritApi.accounts()).thenReturn(accountsMock);
-        return accountsMock;
-    }
-
-    private void mockGerritAccountsQueryApiCall(String username, int expectedAccountId) {
-        AccountState accountStateMock = mock(AccountState.class);
-        Account accountMock = mock(Account.class);
-        when(accountStateMock.account()).thenReturn(accountMock);
-        when(accountMock.id()).thenReturn(Account.id(expectedAccountId));
-        when(accountCacheMock.getByUsername(username)).thenReturn(Optional.of(accountStateMock));
-    }
-
-    private void mockGerritAccountGroupsApiCall(Accounts accountsMock, int accountId)
-        throws RestApiException {
-        Gson gson = OutputFormat.JSON.newGson();
-        List<GroupInfo> groups =
-            gson.fromJson(
-                readTestFile("__files/gerritAccountGroups.json"),
-                new TypeLiteral<List<GroupInfo>>() {}.getType());
-        AccountApi accountApiMock = mock(AccountApi.class);
-        when(accountsMock.id(accountId)).thenReturn(accountApiMock);
-        when(accountApiMock.getGroups()).thenReturn(groups);
-    }
-
-    private void mockGerritChangeDetailsApiCall() throws RestApiException {
-        ChangeInfo changeInfo = readTestFileToClass("__files/gerritPatchSetDetail.json", ChangeInfo.class);
-        when(changeApiMock.get()).thenReturn(changeInfo);
-    }
-
-    private void mockGerritChangeCommentsApiCall() throws RestApiException {
-        Map<String, List<CommentInfo>> comments =
-            readTestFileToType(
-                "__files/gerritPatchSetComments.json",
-                new TypeLiteral<Map<String, List<CommentInfo>>>() {}.getType());
-        when(changeApiMock.commentsRequest()).thenReturn(commentsRequestMock);
-        when(commentsRequestMock.get()).thenReturn(comments);
-    }
-
-    private void mockGerritChangeApiRestEndpoint() throws RestApiException {
-        when(gerritApi.changes()).thenReturn(changesMock);
-        when(changesMock.id(PROJECT_NAME.get(), BRANCH_NAME.shortName(), CHANGE_ID.get())).thenReturn(changeApiMock);
-    }
-
-    private void mockGerritReviewApiCall() throws RestApiException {
-        ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class);
-        when(revisionApiMock.review(reviewInputCaptor.capture())).thenReturn(reviewResult);
-    }
-
-    protected void initComparisonContent() {}
-
-    protected <T> T readTestFileToClass(String filename, Class<T> clazz) {
-        Gson gson = OutputFormat.JSON.newGson();
-        return gson.fromJson(readTestFile(filename), clazz);
-    }
-
-    protected <T> T readTestFileToType(String filename, Type type) {
-        Gson gson = OutputFormat.JSON.newGson();
-        return gson.fromJson(readTestFile(filename), type);
-    }
-
-    protected String readTestFile(String filename) {
-        try {
-            return new String(Files.readAllBytes(basePath.resolve(filename)));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    protected EventHandlerTask.Result handleEventBasedOnType(EventHandlerTask.SupportedEvents triggeredEvent) {
-        Consumer<Event> typeSpecificSetup = getTypeSpecificSetup(triggeredEvent);
-        Event event = getMockedEvent(triggeredEvent);
-        setupCommonEventMocks((PatchSetEvent) event); // Apply common mock configurations
-        typeSpecificSetup.accept(event);
-
-        EventHandlerTask task = Guice.createInjector(new AbstractModule() {
-            @Override
-            protected void configure() {
-                install(new TestGerritEventContextModule(config, event));
-
-                bind(GerritClient.class).toInstance(gerritClient);
-                bind(GitRepoFiles.class).toInstance(gitRepoFiles);
-                bind(ConfigCreator.class).toInstance(mockConfigCreator);
-                bind(PatchSetReviewer.class).toInstance(patchSetReviewer);
-                bind(PluginDataHandlerProvider.class).toInstance(pluginDataHandlerProvider);
-                bind(AccountCache.class).toInstance(mockAccountCache());
-            }
-        }).getInstance(EventHandlerTask.class);
-        return task.execute();
-    }
-
-    protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
-        ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class);
-        verify(revisionApiMock).review(reviewInputCaptor.capture());
-        gptRequestBody = getGson().fromJson(patchSetReviewer.getChatGptClient().getRequestBody(), JsonObject.class);
-        return reviewInputCaptor;
-    }
-
-    protected void initTest() {
-        changeSetData = new ChangeSetData(
-                GPT_USER_ACCOUNT_ID,
-                config.getVotingMinScore(),
-                config.getMaxReviewFileSize()
-        );
-        Localizer localizer = new Localizer(config);
-        gerritClient =
-            new GerritClient(
-                new GerritClientFacade(
-                    config,
-                    changeSetData,
-                    new GerritClientComments(
-                            config,
-                            accountCacheMock,
-                            changeSetData,
-                            pluginDataHandlerProvider,
-                            localizer
-                    ),
-                    getGerritClientPatchSet()));
-        patchSetReviewer =
-            new PatchSetReviewer(
-                gerritClient,
+  protected void initTest() {
+    changeSetData =
+        new ChangeSetData(
+            GPT_USER_ACCOUNT_ID, config.getVotingMinScore(), config.getMaxReviewFileSize());
+    Localizer localizer = new Localizer(config);
+    gerritClient =
+        new GerritClient(
+            new GerritClientFacade(
                 config,
                 changeSetData,
-                Providers.of(new GerritClientReview(config, accountCacheMock, pluginDataHandlerProvider, localizer)),
-                getChatGptClient(),
-                localizer
-            );
-        mockConfigCreator = mock(ConfigCreator.class);
-    }
+                new GerritClientComments(
+                    config, accountCacheMock, changeSetData, pluginDataHandlerProvider, localizer),
+                getGerritClientPatchSet()));
+    patchSetReviewer =
+        new PatchSetReviewer(
+            gerritClient,
+            config,
+            changeSetData,
+            Providers.of(
+                new GerritClientReview(
+                    config, accountCacheMock, pluginDataHandlerProvider, localizer)),
+            getChatGptClient(),
+            localizer);
+    mockConfigCreator = mock(ConfigCreator.class);
+  }
 
-    private AccountAttribute createTestAccountAttribute() {
-        AccountAttribute accountAttribute = new AccountAttribute();
-        accountAttribute.name = GERRIT_USER_ACCOUNT_NAME;
-        accountAttribute.username = GERRIT_USER_USERNAME;
-        accountAttribute.email = GERRIT_USER_ACCOUNT_EMAIL;
-        return accountAttribute;
-    }
+  private AccountAttribute createTestAccountAttribute() {
+    AccountAttribute accountAttribute = new AccountAttribute();
+    accountAttribute.name = GERRIT_USER_ACCOUNT_NAME;
+    accountAttribute.username = GERRIT_USER_USERNAME;
+    accountAttribute.email = GERRIT_USER_ACCOUNT_EMAIL;
+    return accountAttribute;
+  }
 
-    private PatchSetAttribute createPatchSetAttribute() {
-        PatchSetAttribute patchSetAttribute = new PatchSetAttribute();
-        patchSetAttribute.kind = REWORK;
-        patchSetAttribute.author = createTestAccountAttribute();
-        return patchSetAttribute;
-    }
+  private PatchSetAttribute createPatchSetAttribute() {
+    PatchSetAttribute patchSetAttribute = new PatchSetAttribute();
+    patchSetAttribute.kind = REWORK;
+    patchSetAttribute.author = createTestAccountAttribute();
+    return patchSetAttribute;
+  }
 
-    @NonNull
-    private Consumer<Event> getTypeSpecificSetup(EventHandlerTask.SupportedEvents triggeredEvent) {
-        return switch (triggeredEvent) {
-            case COMMENT_ADDED -> event -> {
-                CommentAddedEvent commentEvent = (CommentAddedEvent) event;
-                commentEvent.author = this::createTestAccountAttribute;
-                commentEvent.patchSet = this::createPatchSetAttribute;
-                commentEvent.eventCreatedOn = TEST_TIMESTAMP;
-                when(commentEvent.getType()).thenReturn("comment-added");
-            };
-            case PATCH_SET_CREATED -> event -> {
-                PatchSetCreatedEvent patchEvent = (PatchSetCreatedEvent) event;
-                patchEvent.patchSet = this::createPatchSetAttribute;
-                when(patchEvent.getType()).thenReturn("patchset-created");
-            };
-            case CHANGE_MERGED -> event -> {
-                ChangeMergedEvent mergedEvent = (ChangeMergedEvent) event;
-                when(mergedEvent.getType()).thenReturn("change-merged");
-            };
-        };
-    }
+  @NonNull
+  private Consumer<Event> getTypeSpecificSetup(EventHandlerTask.SupportedEvents triggeredEvent) {
+    return switch (triggeredEvent) {
+      case COMMENT_ADDED ->
+          event -> {
+            CommentAddedEvent commentEvent = (CommentAddedEvent) event;
+            commentEvent.author = this::createTestAccountAttribute;
+            commentEvent.patchSet = this::createPatchSetAttribute;
+            commentEvent.eventCreatedOn = TEST_TIMESTAMP;
+            when(commentEvent.getType()).thenReturn("comment-added");
+          };
+      case PATCH_SET_CREATED ->
+          event -> {
+            PatchSetCreatedEvent patchEvent = (PatchSetCreatedEvent) event;
+            patchEvent.patchSet = this::createPatchSetAttribute;
+            when(patchEvent.getType()).thenReturn("patchset-created");
+          };
+      case CHANGE_MERGED ->
+          event -> {
+            ChangeMergedEvent mergedEvent = (ChangeMergedEvent) event;
+            when(mergedEvent.getType()).thenReturn("change-merged");
+          };
+    };
+  }
 
-    private Event getMockedEvent(EventHandlerTask.SupportedEvents triggeredEvent) {
-        return (Event) mock(EVENT_CLASS_MAP.get(triggeredEvent));
-    }
+  private Event getMockedEvent(EventHandlerTask.SupportedEvents triggeredEvent) {
+    return (Event) mock(EVENT_CLASS_MAP.get(triggeredEvent));
+  }
 
-    private void setupCommonEventMocks(PatchSetEvent event) {
-        when(event.getProjectNameKey()).thenReturn(PROJECT_NAME);
-        when(event.getBranchNameKey()).thenReturn(BRANCH_NAME);
-        when(event.getChangeKey()).thenReturn(CHANGE_ID);
-    }
+  private void setupCommonEventMocks(PatchSetEvent event) {
+    when(event.getProjectNameKey()).thenReturn(PROJECT_NAME);
+    when(event.getBranchNameKey()).thenReturn(BRANCH_NAME);
+    when(event.getChangeKey()).thenReturn(CHANGE_ID);
+  }
 
-    private AccountCache mockAccountCache() {
-        AccountCache accountCache = mock(AccountCache.class);
-        Account account = Account.builder(Account.id(GPT_USER_ACCOUNT_ID), Instant.now()).build();
-        AccountState accountState = AccountState.forAccount(account, Collections.emptyList());
-        doReturn(Optional.of(accountState)).when(accountCache).getByUsername(GERRIT_GPT_USERNAME);
+  private AccountCache mockAccountCache() {
+    AccountCache accountCache = mock(AccountCache.class);
+    Account account = Account.builder(Account.id(GPT_USER_ACCOUNT_ID), Instant.now()).build();
+    AccountState accountState = AccountState.forAccount(account, Collections.emptyList());
+    doReturn(Optional.of(accountState)).when(accountCache).getByUsername(GERRIT_GPT_USERNAME);
 
-        return accountCache;
-    }
+    return accountCache;
+  }
 
-    private ChatAIClient getChatGptClient() {
-        return switch (config.getAIMode()) {
-            case stateful -> new AIChatClientStateful(config, gitRepoFiles, pluginDataHandlerProvider);
-            case stateless -> new AIChatClientStateless(config);
-        };
-    }
+  private ChatAIClient getChatGptClient() {
+    return switch (config.getAIMode()) {
+      case stateful -> new AIChatClientStateful(config, gitRepoFiles, pluginDataHandlerProvider);
+      case stateless -> new AIChatClientStateless(config);
+    };
+  }
 
-    private GerritClientPatchSet getGerritClientPatchSet() {
-        return switch (config.getAIMode()) {
-            case stateful -> new GerritClientPatchSetStateful(config, accountCacheMock);
-            case stateless -> new GerritClientPatchSetStateless(config, accountCacheMock);
-        };
-    }
+  private GerritClientPatchSet getGerritClientPatchSet() {
+    return switch (config.getAIMode()) {
+      case stateful -> new GerritClientPatchSetStateful(config, accountCacheMock);
+      case stateless -> new GerritClientPatchSetStateless(config, accountCacheMock);
+    };
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatTestBase.java
index 7e6aff2..a1ed09c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/AIChatTestBase.java
@@ -1,39 +1,52 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
+import static org.mockito.Mockito.when;
+
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.client.api.gerrit.GerritChange;
+import java.nio.file.Path;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
 import org.mockito.Mock;
 
-import java.nio.file.Path;
-
-import static org.mockito.Mockito.when;
-
 public class AIChatTestBase {
-    protected static final Project.NameKey PROJECT_NAME = Project.NameKey.parse("myProject");
-    protected static final Change.Key CHANGE_ID = Change.Key.parse("myChangeId");
-    protected static final BranchNameKey BRANCH_NAME = BranchNameKey.create(PROJECT_NAME, "myBranchName");
+  protected static final Project.NameKey PROJECT_NAME = Project.NameKey.parse("myProject");
+  protected static final Change.Key CHANGE_ID = Change.Key.parse("myChangeId");
+  protected static final BranchNameKey BRANCH_NAME =
+      BranchNameKey.create(PROJECT_NAME, "myBranchName");
 
-    @Rule
-    public TemporaryFolder tempFolder = new TemporaryFolder();
+  @Rule public TemporaryFolder tempFolder = new TemporaryFolder();
 
-    @Mock
-    protected Path mockPluginDataPath;
+  @Mock protected Path mockPluginDataPath;
 
-    protected Path realPluginDataPath;
+  protected Path realPluginDataPath;
 
-    protected void setupPluginData() {
-        realPluginDataPath = tempFolder.getRoot().toPath().resolve("global.data");
-        Path realProjectDataPath = tempFolder.getRoot().toPath().resolve(PROJECT_NAME + ".data");
+  protected void setupPluginData() {
+    realPluginDataPath = tempFolder.getRoot().toPath().resolve("global.data");
+    Path realProjectDataPath = tempFolder.getRoot().toPath().resolve(PROJECT_NAME + ".data");
 
-        // Mock the PluginData annotation project behavior
-        when(mockPluginDataPath.resolve(PROJECT_NAME + ".data")).thenReturn(realProjectDataPath);
-    }
+    // Mock the PluginData annotation project behavior
+    when(mockPluginDataPath.resolve(PROJECT_NAME + ".data")).thenReturn(realProjectDataPath);
+  }
 
-    protected GerritChange getGerritChange() {
-        return new GerritChange(AIChatTestBase.PROJECT_NAME, AIChatTestBase.BRANCH_NAME, AIChatTestBase.CHANGE_ID);
-    }
+  protected GerritChange getGerritChange() {
+    return new GerritChange(
+        AIChatTestBase.PROJECT_NAME, AIChatTestBase.BRANCH_NAME, AIChatTestBase.CHANGE_ID);
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/PluginDataTest.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/PluginDataTest.java
index 5fee984..d850736 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/PluginDataTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/PluginDataTest.java
@@ -1,12 +1,27 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
-import java.nio.file.Files;
-
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
+import java.nio.file.Files;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -15,58 +30,65 @@
 @RunWith(MockitoJUnitRunner.class)
 public class PluginDataTest extends AIChatTestBase {
 
-    @Before
-    public void setUp() {
-        setupPluginData();
+  @Before
+  public void setUp() {
+    setupPluginData();
 
-        // Mock the PluginData annotation global behavior
-        when(mockPluginDataPath.resolve("global.data")).thenReturn(realPluginDataPath);
-    }
+    // Mock the PluginData annotation global behavior
+    when(mockPluginDataPath.resolve("global.data")).thenReturn(realPluginDataPath);
+  }
 
-    @Test
-    public void testValueSetAndGet() {
-        PluginDataHandlerProvider provider = new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
-        PluginDataHandler globalHandler = provider.getGlobalScope();
-        PluginDataHandler projectHandler = provider.getProjectScope();
+  @Test
+  public void testValueSetAndGet() {
+    PluginDataHandlerProvider provider =
+        new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
+    PluginDataHandler globalHandler = provider.getGlobalScope();
+    PluginDataHandler projectHandler = provider.getProjectScope();
 
-        String key = "testKey";
-        String value = "testValue";
+    String key = "testKey";
+    String value = "testValue";
 
-        // Test set value
-        globalHandler.setValue(key, value);
-        projectHandler.setValue(key, value);
+    // Test set value
+    globalHandler.setValue(key, value);
+    projectHandler.setValue(key, value);
 
-        // Test get value
-        assertEquals("The value retrieved should match the value set.", value, globalHandler.getValue(key));
-        assertEquals("The value retrieved should match the value set.", value, projectHandler.getValue(key));
-    }
+    // Test get value
+    assertEquals(
+        "The value retrieved should match the value set.", value, globalHandler.getValue(key));
+    assertEquals(
+        "The value retrieved should match the value set.", value, projectHandler.getValue(key));
+  }
 
-    @Test
-    public void testRemoveValue() {
-        PluginDataHandlerProvider provider = new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
-        PluginDataHandler handler = provider.getGlobalScope();
+  @Test
+  public void testRemoveValue() {
+    PluginDataHandlerProvider provider =
+        new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
+    PluginDataHandler handler = provider.getGlobalScope();
 
-        String key = "testKey";
-        String value = "testValue";
+    String key = "testKey";
+    String value = "testValue";
 
-        // Set a value to ensure it can be removed
-        handler.setValue(key, value);
-        // Remove the value
-        handler.removeValue(key);
+    // Set a value to ensure it can be removed
+    handler.setValue(key, value);
+    // Remove the value
+    handler.removeValue(key);
 
-        // Verify the value is no longer available
-        assertNull("The value should be null after being removed.", handler.getValue(key));
-    }
+    // Verify the value is no longer available
+    assertNull("The value should be null after being removed.", handler.getValue(key));
+  }
 
-    @Test
-    public void testCreateFileOnNonexistent() throws Exception {
-        // Ensure the file doesn't exist before creating the handler
-        Files.deleteIfExists(realPluginDataPath);
+  @Test
+  public void testCreateFileOnNonexistent() throws Exception {
+    // Ensure the file doesn't exist before creating the handler
+    Files.deleteIfExists(realPluginDataPath);
 
-        PluginDataHandlerProvider provider = new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
-        provider.getGlobalScope();
+    PluginDataHandlerProvider provider =
+        new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
+    provider.getGlobalScope();
 
-        // The constructor should create the file if it doesn't exist
-        assertTrue("The config file should exist after initializing the handler.", Files.exists(realPluginDataPath));
-    }
+    // The constructor should create the file if it doesn't exist
+    assertTrue(
+        "The config file should exist after initializing the handler.",
+        Files.exists(realPluginDataPath));
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TemporaryFileTest.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TemporaryFileTest.java
index 30be370..b44a0c6 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TemporaryFileTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TemporaryFileTest.java
@@ -1,35 +1,48 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
+import com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-
-import com.googlesource.gerrit.plugins.aicodereview.utils.FileUtils;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
 public class TemporaryFileTest {
-    @Rule
-    public TemporaryFolder tempFolder = new TemporaryFolder();
+  @Rule public TemporaryFolder tempFolder = new TemporaryFolder();
 
-    @Test
-    public void testCreateTempFileWithContent() throws IOException {
-        // Prepare
-        String prefix = "testFile";
-        String suffix = ".txt";
-        String content = "This is a test content";
+  @Test
+  public void testCreateTempFileWithContent() throws IOException {
+    // Prepare
+    String prefix = "testFile";
+    String suffix = ".txt";
+    String content = "This is a test content";
 
-        // Execute
-        Path tempFile = FileUtils.createTempFileWithContent(prefix, suffix, content);
+    // Execute
+    Path tempFile = FileUtils.createTempFileWithContent(prefix, suffix, content);
 
-        // Verify the file exists
-        assertTrue("Temporary file should exist", Files.exists(tempFile));
+    // Verify the file exists
+    assertTrue("Temporary file should exist", Files.exists(tempFile));
 
-        // Read back the content
-        String fileContent = Files.readString(tempFile);
-        assertEquals("File content should match", content, fileContent);
-    }
+    // Read back the content
+    String fileContent = Files.readString(tempFile);
+    assertEquals("File content should match", content, fileContent);
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TestGerritEventContextModule.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TestGerritEventContextModule.java
index 47dd921..917c985 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TestGerritEventContextModule.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/TestGerritEventContextModule.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview;
 
 import com.google.gerrit.extensions.annotations.PluginData;
@@ -5,19 +19,18 @@
 import com.google.inject.Provides;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.listener.GerritEventContextModule;
-
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
 public class TestGerritEventContextModule extends GerritEventContextModule {
 
-    public TestGerritEventContextModule(Configuration config, Event event) {
-        super(config, event);
-    }
+  public TestGerritEventContextModule(Configuration config, Event event) {
+    super(config, event);
+  }
 
-    @Provides
-    @PluginData
-    Path providePluginDataPath() {
-        return Paths.get(System.getProperty("pluginDataPath", "test-plugin-data"));
-    }
+  @Provides
+  @PluginData
+  Path providePluginDataPath() {
+    return Paths.get(System.getProperty("pluginDataPath", "test-plugin-data"));
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/integration/CodeReviewPluginIT.java b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/integration/CodeReviewPluginIT.java
index f196ce4..962eb61 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/aicodereview/integration/CodeReviewPluginIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/aicodereview/integration/CodeReviewPluginIT.java
@@ -1,5 +1,22 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package com.googlesource.gerrit.plugins.aicodereview.integration;
 
+import static junit.framework.TestCase.assertNotNull;
+import static org.mockito.Mockito.when;
+
 import com.google.gerrit.server.account.AccountCache;
 import com.googlesource.gerrit.plugins.aicodereview.config.Configuration;
 import com.googlesource.gerrit.plugins.aicodereview.data.PluginDataHandlerProvider;
@@ -12,6 +29,8 @@
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.data.ChangeSetData;
 import com.googlesource.gerrit.plugins.aicodereview.mode.common.model.review.ReviewBatch;
 import com.googlesource.gerrit.plugins.aicodereview.mode.stateless.client.prompt.AIChatPromptStateless;
+import java.util.ArrayList;
+import java.util.List;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -20,65 +39,59 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import static junit.framework.TestCase.assertNotNull;
-import static org.mockito.Mockito.when;
-
-@Ignore("This test suite is designed to demonstrate how to test the Gerrit and GPT interfaces in a real environment. " +
-        "It is not intended to be executed during the regular build process")
+@Ignore(
+    "This test suite is designed to demonstrate how to test the Gerrit and GPT interfaces in a real"
+        + " environment. It is not intended to be executed during the regular build process")
 @Slf4j
 @RunWith(MockitoJUnitRunner.class)
 public class CodeReviewPluginIT {
-    @Mock
-    private Configuration config;
+  @Mock private Configuration config;
 
-    @Mock
-    protected PluginDataHandlerProvider pluginDataHandlerProvider;
+  @Mock protected PluginDataHandlerProvider pluginDataHandlerProvider;
 
-    @InjectMocks
-    private GerritClient gerritClient;
+  @InjectMocks private GerritClient gerritClient;
 
-    @InjectMocks
-    private ChatAIClient chatGptClient;
+  @InjectMocks private ChatAIClient chatGptClient;
 
-    @InjectMocks
-    private AccountCache accountCache;
+  @InjectMocks private AccountCache accountCache;
 
-    @Test
-    public void sayHelloToGPT() throws Exception {
-        ChangeSetData changeSetData = new ChangeSetData(1, config.getVotingMinScore(), config.getMaxReviewFileSize());
-        AIChatPromptStateless AIChatPromptStateless = new AIChatPromptStateless(config, true);
-        when(config.getAIDomain()).thenReturn(Configuration.OPENAI_DOMAIN);
-        when(config.getAIToken()).thenReturn("Your GPT token");
-        when(config.getAIModel()).thenReturn(Configuration.DEFAULT_CHATGPT_MODEL);
-        when(AIChatPromptStateless.getAISystemPrompt()).thenReturn(AIChatPromptStateless.DEFAULT_AI_CHAT_SYSTEM_PROMPT);
+  @Test
+  public void sayHelloToGPT() throws Exception {
+    ChangeSetData changeSetData =
+        new ChangeSetData(1, config.getVotingMinScore(), config.getMaxReviewFileSize());
+    AIChatPromptStateless AIChatPromptStateless = new AIChatPromptStateless(config, true);
+    when(config.getAIDomain()).thenReturn(Configuration.OPENAI_DOMAIN);
+    when(config.getAIToken()).thenReturn("Your GPT token");
+    when(config.getAIModel()).thenReturn(Configuration.DEFAULT_CHATGPT_MODEL);
+    when(AIChatPromptStateless.getAISystemPrompt())
+        .thenReturn(AIChatPromptStateless.DEFAULT_AI_CHAT_SYSTEM_PROMPT);
 
-        AIChatResponseContent answer = chatGptClient.ask(changeSetData, new GerritChange(""), "hello");
-        log.info("answer: {}", answer);
-        assertNotNull(answer);
-    }
+    AIChatResponseContent answer = chatGptClient.ask(changeSetData, new GerritChange(""), "hello");
+    log.info("answer: {}", answer);
+    assertNotNull(answer);
+  }
 
-    @Test
-    public void getPatchSet() throws Exception {
-        when(config.getGerritUserName()).thenReturn("Your Gerrit username");
+  @Test
+  public void getPatchSet() throws Exception {
+    when(config.getGerritUserName()).thenReturn("Your Gerrit username");
 
-        String patchSet = gerritClient.getPatchSet("${changeId}");
-        log.info("patchSet: {}", patchSet);
-        assertNotNull(patchSet);
-    }
+    String patchSet = gerritClient.getPatchSet("${changeId}");
+    log.info("patchSet: {}", patchSet);
+    assertNotNull(patchSet);
+  }
 
-    @Test
-    public void setReview() throws Exception {
-        ChangeSetData changeSetData = new ChangeSetData(1, config.getVotingMinScore(), config.getMaxReviewFileSize());
-        Localizer localizer = new Localizer(config);
-        when(config.getGerritUserName()).thenReturn("Your Gerrit username");
+  @Test
+  public void setReview() throws Exception {
+    ChangeSetData changeSetData =
+        new ChangeSetData(1, config.getVotingMinScore(), config.getMaxReviewFileSize());
+    Localizer localizer = new Localizer(config);
+    when(config.getGerritUserName()).thenReturn("Your Gerrit username");
 
-        List<ReviewBatch> reviewBatches = new ArrayList<>();
-        reviewBatches.add(new ReviewBatch("message"));
+    List<ReviewBatch> reviewBatches = new ArrayList<>();
+    reviewBatches.add(new ReviewBatch("message"));
 
-        GerritClientReview gerritClientReview = new GerritClientReview(config, accountCache, pluginDataHandlerProvider, localizer);
-        gerritClientReview.setReview(new GerritChange("Your changeId"), reviewBatches, changeSetData);
-    }
+    GerritClientReview gerritClientReview =
+        new GerritClientReview(config, accountCache, pluginDataHandlerProvider, localizer);
+    gerritClientReview.setReview(new GerritChange("Your changeId"), reviewBatches, changeSetData);
+  }
 }